diff --git a/CHANGELOG.md b/CHANGELOG.md index 83d5f68..7d9485f 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -3,6 +3,11 @@ All notable changes to this project will be documented in this file. -## [v0.0.1] - 2024-XX-XX +## [v0.0.1] - 2026-03-03 -- First alpha release +- First alpha release (homemade QUIC stack) + +## [v0.0.2] - XXXX-XX-XX + +- Switched to picoquic as the underlying QUIC stack +- Added support for external logging functions diff --git a/examples/moq-interop-test.c b/examples/moq-interop-test.c index 7cd9d6a..b820c57 100644 --- a/examples/moq-interop-test.c +++ b/examples/moq-interop-test.c @@ -84,6 +84,7 @@ typedef struct imquic_moq_interop_client { imquic_client *client; imquic_connection *conn; gboolean publisher; + uint64_t request_id; } imquic_moq_interop_client; static void imquic_moq_interop_client_destroy(imquic_moq_interop_client *mc) { if(mc != NULL) { @@ -136,18 +137,17 @@ static imquic_moq_interop_client *imquic_moq_interop_client_create(imquic_moq_in /* Callbacks */ static void imquic_moq_interop_new_connection(imquic_connection *conn, void *user_data); -static void imquic_moq_interop_connection_failed(void *user_data); static void imquic_moq_interop_ready(imquic_connection *conn); static void imquic_moq_interop_publish_namespace_accepted(imquic_connection *conn, uint64_t request_id, imquic_moq_request_parameters *parameters); static void imquic_moq_interop_publish_namespace_error(imquic_connection *conn, uint64_t request_id, imquic_moq_request_error_code error_code, const char *reason, uint64_t retry_interval); -static void imquic_moq_interop_incoming_subscribe(imquic_connection *conn, uint64_t request_id, - uint64_t track_alias, imquic_moq_namespace *tns, imquic_moq_name *tn, imquic_moq_request_parameters *parameters); +static void imquic_moq_interop_incoming_subscribe(imquic_connection *conn, uint64_t request_id, uint64_t required_id_delta, + imquic_moq_namespace *tns, imquic_moq_name *tn, imquic_moq_request_parameters *parameters); static void imquic_moq_interop_subscribe_accepted(imquic_connection *conn, uint64_t request_id, - uint64_t track_alias, imquic_moq_request_parameters *parameters, GList *track_extensions); + uint64_t track_alias, imquic_moq_request_parameters *parameters, GList *track_properties); static void imquic_moq_interop_subscribe_error(imquic_connection *conn, uint64_t request_id, - imquic_moq_request_error_code error_code, const char *reason, uint64_t track_alias, uint64_t retry_interval); + imquic_moq_request_error_code error_code, const char *reason, uint64_t retry_interval); static void imquic_moq_interop_connection_gone(imquic_connection *conn); /* Main */ @@ -454,7 +454,6 @@ static imquic_moq_interop_client *imquic_moq_interop_client_create(imquic_moq_in return NULL; } imquic_set_new_moq_connection_cb(mc->client, imquic_moq_interop_new_connection); - imquic_set_connection_failed_cb(mc->client, imquic_moq_interop_connection_failed); imquic_set_moq_ready_cb(mc->client, imquic_moq_interop_ready); imquic_set_moq_connection_gone_cb(mc->client, imquic_moq_interop_connection_gone); if(publisher) { @@ -481,14 +480,6 @@ static void imquic_moq_interop_new_connection(imquic_connection *conn, void *use imquic_moq_set_max_request_id(conn, max_request_id); } -static void imquic_moq_interop_connection_failed(void *user_data) { - imquic_moq_interop_client *client = (imquic_moq_interop_client *)user_data; - if(client != NULL) { - imquic_moq_interop_test_context *test = (imquic_moq_interop_test_context *)client->test; - g_atomic_int_set(&test->done, 1); - } -} - static void imquic_moq_interop_ready(imquic_connection *conn) { /* Depending on the test, we may or may not be done */ imquic_mutex_lock(&mutex); @@ -525,7 +516,8 @@ static void imquic_moq_interop_ready(imquic_connection *conn) { tns[1].buffer = (uint8_t *)"interop"; tns[1].length = strlen("interop"); tns[1].next = NULL; - imquic_moq_publish_namespace(conn, imquic_moq_get_next_request_id(conn), &tns[0], NULL); + client->request_id = imquic_moq_get_next_request_id(conn); + imquic_moq_publish_namespace(conn, client->request_id, 0, &tns[0], NULL); if(verbose) test->subtests = g_list_append(test->subtests, g_strdup("publisher announced namespace")); } else if(test->name == IMQUIC_INTEROP_SUBSCRIBE_ERROR) { @@ -541,7 +533,8 @@ static void imquic_moq_interop_ready(imquic_connection *conn) { .buffer = (uint8_t *)"test-track", .length = strlen("test-track") }; - imquic_moq_subscribe(conn, imquic_moq_get_next_request_id(conn), 0, &tns[0], &tn, NULL); + client->request_id = imquic_moq_get_next_request_id(conn); + imquic_moq_subscribe(conn, client->request_id, 0, &tns[0], &tn, NULL); if(verbose) test->subtests = g_list_append(test->subtests, g_strdup("subscriber subscribed to non-existing track")); } else if((test->name == IMQUIC_INTEROP_ANNOUNCE_SUBSCRIBE || @@ -558,7 +551,8 @@ static void imquic_moq_interop_ready(imquic_connection *conn) { .buffer = (uint8_t *)"test-track", .length = strlen("test-track") }; - imquic_moq_subscribe(conn, imquic_moq_get_next_request_id(conn), 0, &tns[0], &tn, NULL); + client->request_id = imquic_moq_get_next_request_id(conn); + imquic_moq_subscribe(conn, client->request_id, 0, &tns[0], &tn, NULL); if(verbose) test->subtests = g_list_append(test->subtests, g_strdup("subscriber subscribed to track")); if(test->name == IMQUIC_INTEROP_SUBSCRIBE_BEFORE_ANNOUNCE) { @@ -584,14 +578,7 @@ static void imquic_moq_interop_publish_namespace_accepted(imquic_connection *con g_atomic_int_set(&test->done, 1); } else if(test->name == IMQUIC_INTEROP_PUBLISH_NAMESPACE_DONE) { /* Send a PUBLISH_NAMESPACE_DONE */ - imquic_moq_namespace tns[2]; - tns[0].buffer = (uint8_t *)"moq-test"; - tns[0].length = strlen("moq-test"); - tns[0].next = &tns[1]; - tns[1].buffer = (uint8_t *)"interop"; - tns[1].length = strlen("interop"); - tns[1].next = NULL; - int ret = imquic_moq_publish_namespace_done(conn, &tns[0]); + int ret = imquic_moq_publish_namespace_done(conn, client->request_id); if(ret == 0) { g_atomic_int_set(&test->success, 1); if(verbose) @@ -629,8 +616,8 @@ static void imquic_moq_interop_publish_namespace_error(imquic_connection *conn, /* TODO Other tests */ } -static void imquic_moq_interop_incoming_subscribe(imquic_connection *conn, uint64_t request_id, - uint64_t track_alias, imquic_moq_namespace *tns, imquic_moq_name *tn, imquic_moq_request_parameters *parameters) { +static void imquic_moq_interop_incoming_subscribe(imquic_connection *conn, uint64_t request_id, uint64_t required_id_delta, + imquic_moq_namespace *tns, imquic_moq_name *tn, imquic_moq_request_parameters *parameters) { /* Depending on the test, we may or may not be done */ imquic_mutex_lock(&mutex); imquic_moq_interop_client *client = (imquic_moq_interop_client *)g_hash_table_lookup(connections, conn); @@ -647,7 +634,7 @@ static void imquic_moq_interop_incoming_subscribe(imquic_connection *conn, uint6 } static void imquic_moq_interop_subscribe_accepted(imquic_connection *conn, uint64_t request_id, - uint64_t track_alias, imquic_moq_request_parameters *parameters, GList *track_extensions) { + uint64_t track_alias, imquic_moq_request_parameters *parameters, GList *track_properties) { /* Depending on the test, we may or may not be done */ imquic_mutex_lock(&mutex); imquic_moq_interop_client *client = (imquic_moq_interop_client *)g_hash_table_lookup(connections, conn); @@ -672,7 +659,7 @@ static void imquic_moq_interop_subscribe_accepted(imquic_connection *conn, uint6 } static void imquic_moq_interop_subscribe_error(imquic_connection *conn, uint64_t request_id, - imquic_moq_request_error_code error_code, const char *reason, uint64_t track_alias, uint64_t retry_interval) { + imquic_moq_request_error_code error_code, const char *reason, uint64_t retry_interval) { /* Depending on the test, we may or may not be done */ imquic_mutex_lock(&mutex); imquic_moq_interop_client *client = (imquic_moq_interop_client *)g_hash_table_lookup(connections, conn); diff --git a/examples/moq-pub-options.c b/examples/moq-pub-options.c index 8e8a517..1cd875e 100644 --- a/examples/moq-pub-options.c +++ b/examples/moq-pub-options.c @@ -15,17 +15,17 @@ static GOptionContext *opts = NULL; gboolean demo_options_parse(demo_options *options, int argc, char *argv[]) { /* Supported command-line arguments */ GOptionEntry opt_entries[] = { - { "moq-draft-version", 'M', 0, G_OPTION_ARG_STRING, &options->moq_version, "MoQ draft version number to negotiate (default=any)", "|any|legacy" }, + { "moq-draft-version", 'M', 0, G_OPTION_ARG_STRING, &options->moq_version, "MoQ draft version number to negotiate (default=any)", "|any" }, { "track-namespace", 'n', 0, G_OPTION_ARG_STRING_ARRAY, &options->track_namespace, "MoQ track namespace to publish (can be called multiple times to create a tuple; default=none)", "namespace" }, { "track-name", 'N', 0, G_OPTION_ARG_STRING, &options->track_name, "MoQ track name to publish (default=none)", "name" }, - { "first-group", 'f', 0, G_OPTION_ARG_INT64, &options->first_group, "First group ID to send (default=0; sends 'Prior Group ID Gap' extension for the first sent object in that group, if set))", "group_id" }, - { "first-object", 'F', 0, G_OPTION_ARG_INT64, &options->first_object, "First object ID to send (default=0; sends 'Prior Object ID Gap' extension for the first sent object in that group, if set))", "object_id" }, + { "first-group", 'f', 0, G_OPTION_ARG_INT64, &options->first_group, "First group ID to send (default=0; sends 'Prior Group ID Gap' property for the first sent object in that group, if set))", "group_id" }, + { "first-object", 'F', 0, G_OPTION_ARG_INT64, &options->first_object, "First object ID to send (default=0; sends 'Prior Object ID Gap' property for the first sent object in that group, if set))", "object_id" }, { "relay-auth-info", 'a', 0, G_OPTION_ARG_STRING, &options->relay_auth_info, "Auth info required to connect to the relay, if any (default=none)", "string" }, { "auth-info", 'A', 0, G_OPTION_ARG_STRING, &options->auth_info, "Auth info to publish_namespace/publish, if needed (default=none)", "string" }, { "delivery", 'D', 0, G_OPTION_ARG_STRING, &options->delivery, "How MoQ objects should be sent (default=subgroup; supported=datagram,subgroup)", "type" }, { "publish", 'X', 0, G_OPTION_ARG_NONE, &options->publish, "Use a PUBLISH right away instead of waiting for a SUBSCRIBE (default=no; only supported for v12 and later)", NULL }, { "track-alias", 't', 0, G_OPTION_ARG_INT64, &options->track_alias, "Track alias to use for subscriptions (default=0; only supported for v12 and later)", NULL }, - { "extensions", 'x', 0, G_OPTION_ARG_NONE, &options->extensions, "Send some extensions along objects (default=no)", NULL }, + { "properties", 'x', 0, G_OPTION_ARG_NONE, &options->properties, "Send some properties along objects (default=no)", NULL }, { "bind", 'b', 0, G_OPTION_ARG_STRING, &options->ip, "Local IP address to bind to (default=all interfaces)", "IP" }, { "port", 'p', 0, G_OPTION_ARG_INT, &options->port, "Local port to bind to (default=0, random)", "port" }, { "remote-host", 'r', 0, G_OPTION_ARG_STRING, &options->remote_host, "QUIC server to connect to (default=none)", "IP" }, diff --git a/examples/moq-pub-options.h b/examples/moq-pub-options.h index 5c8c83a..3807a1a 100644 --- a/examples/moq-pub-options.h +++ b/examples/moq-pub-options.h @@ -27,7 +27,7 @@ typedef struct demo_options { const char *delivery; gboolean publish; uint64_t track_alias; - gboolean extensions; + gboolean properties; const char *ip; int port; const char *remote_host; diff --git a/examples/moq-pub.c b/examples/moq-pub.c index 7c90cf2..65706b2 100644 --- a/examples/moq-pub.c +++ b/examples/moq-pub.c @@ -41,7 +41,7 @@ static void imquic_demo_handle_signal(int signum) { /* Publisher state */ static imquic_connection *moq_conn = NULL; static imquic_moq_version moq_version = IMQUIC_MOQ_VERSION_ANY; -static uint64_t moq_request_id = 0, moq_track_alias = 0; +static uint64_t moq_tns_request_id = 0, moq_request_id = 0, moq_track_alias = 0; static imquic_moq_delivery delivery = IMQUIC_MOQ_USE_SUBGROUP; static char pub_tns_buffer[256], pub_tn_buffer[256]; static const char *pub_tns = NULL, *pub_tn = NULL; @@ -119,15 +119,10 @@ static void imquic_demo_ready(imquic_connection *conn) { imquic_get_connection_name(conn)); } } - imquic_moq_publish_namespace(conn, imquic_moq_get_next_request_id(conn), &tns[0], ¶ms); + moq_tns_request_id = imquic_moq_get_next_request_id(conn); + imquic_moq_publish_namespace(conn, moq_tns_request_id, 0, &tns[0], ¶ms); } else { /* We use PUBLISH */ - if(moq_version < IMQUIC_MOQ_VERSION_12) { - /* Version is too old, we can't: stop here */ - IMQUIC_LOG(IMQUIC_LOG_FATAL, "PUBLISH only supported starting from version 12\n"); - g_atomic_int_inc(&stop); - return; - } IMQUIC_LOG(IMQUIC_LOG_INFO, "[%s] Publishing namespace/track '%s--%s'\n", imquic_get_connection_name(conn), pub_tns, pub_tn); moq_request_id = imquic_moq_get_next_request_id(conn); gboolean forward = FALSE; @@ -147,18 +142,7 @@ static void imquic_demo_ready(imquic_connection *conn) { imquic_get_connection_name(conn)); } } - /* Add a couple of track extensions too (will be ignored on versions older than v16) */ - GList *exts = NULL; - imquic_moq_object_extension default_priority = { 0 }; - default_priority.id = IMQUIC_MOQ_EXT_DEFAULT_PUBLISHER_PRIORITY; - default_priority.value.number = 128; - exts = g_list_append(exts, &default_priority); - imquic_moq_object_extension default_order = { 0 }; - default_order.id = IMQUIC_MOQ_EXT_DEFAULT_GROUP_ORDER; - default_order.value.number = IMQUIC_MOQ_ORDERING_ASCENDING; - exts = g_list_append(exts, &default_order); - imquic_moq_publish(conn, moq_request_id, &tns[0], &tn, moq_track_alias, ¶ms, exts); - g_list_free(exts); + imquic_moq_publish(conn, moq_request_id, 0, &tns[0], &tn, moq_track_alias, ¶ms, NULL); } } @@ -192,29 +176,23 @@ static void imquic_demo_publish_error(imquic_connection *conn, uint64_t request_ g_atomic_int_inc(&stop); } -static void imquic_demo_incoming_subscribe(imquic_connection *conn, uint64_t request_id, uint64_t track_alias, +static void imquic_demo_incoming_subscribe(imquic_connection *conn, uint64_t request_id, uint64_t required_id_delta, imquic_moq_namespace *tns, imquic_moq_name *tn, imquic_moq_request_parameters *parameters) { char tns_buffer[256], tn_buffer[256]; const char *ns = imquic_moq_namespace_str(tns, tns_buffer, sizeof(tns_buffer), TRUE); const char *name = imquic_moq_track_str(tn, tn_buffer, sizeof(tn_buffer)); - if(imquic_moq_get_version(conn) < IMQUIC_MOQ_VERSION_12) { - /* Older versions of MoQ expect the track alias in the SUBSCRIBE */ - IMQUIC_LOG(IMQUIC_LOG_INFO, "[%s] Incoming subscribe for '%s--%s' (ID %"SCNu64"/%"SCNu64")\n", - imquic_get_connection_name(conn), ns, name, request_id, track_alias); - } else { - IMQUIC_LOG(IMQUIC_LOG_INFO, "[%s] Incoming subscribe for '%s--%s' (ID %"SCNu64")\n", - imquic_get_connection_name(conn), ns, name, request_id); - } + IMQUIC_LOG(IMQUIC_LOG_INFO, "[%s] Incoming subscribe for '%s--%s' (ID %"SCNu64")\n", + imquic_get_connection_name(conn), ns, name, request_id); if(pub_tns == NULL || strcasecmp(ns, pub_tns) || strcasecmp(name, pub_tn)) { IMQUIC_LOG(IMQUIC_LOG_WARN, "[%s] Unknown namespace or track\n", imquic_get_connection_name(conn)); - imquic_moq_reject_subscribe(conn, request_id, IMQUIC_MOQ_REQERR_DOES_NOT_EXIST, "Unknown namespace or track", track_alias, 0); + imquic_moq_reject_subscribe(conn, request_id, IMQUIC_MOQ_REQERR_DOES_NOT_EXIST, "Unknown namespace or track", 0); return; } if(options.publish || g_atomic_int_get(&send_objects)) { /* FIXME In this demo, we only allow one subscriber at a time, * as we expect a relay to mediate between us and subscribers */ IMQUIC_LOG(IMQUIC_LOG_WARN, "[%s] We already have a subscriber\n", imquic_get_connection_name(conn)); - imquic_moq_reject_subscribe(conn, request_id, IMQUIC_MOQ_REQERR_DUPLICATE_SUBSCRIPTION, "We already have a subscriber", track_alias, 0); + imquic_moq_reject_subscribe(conn, request_id, IMQUIC_MOQ_REQERR_DUPLICATE_SUBSCRIPTION, "We already have a subscriber", 0); return; } /* TODO Check if it matches our published namespace */ @@ -256,8 +234,6 @@ static void imquic_demo_incoming_subscribe(imquic_connection *conn, uint64_t req } /* Accept the subscription */ moq_request_id = request_id; - if(moq_version < IMQUIC_MOQ_VERSION_12) - moq_track_alias = track_alias; imquic_moq_request_parameters rparams; imquic_moq_request_parameters_init_defaults(&rparams); rparams.expires_set = TRUE; @@ -268,18 +244,7 @@ static void imquic_demo_incoming_subscribe(imquic_connection *conn, uint64_t req rparams.largest_object_set = TRUE; rparams.largest_object = sub_start; } - /* Add a couple of track extensions too (will be ignored on versions older than v16) */ - GList *exts = NULL; - imquic_moq_object_extension default_priority = { 0 }; - default_priority.id = IMQUIC_MOQ_EXT_DEFAULT_PUBLISHER_PRIORITY; - default_priority.value.number = 128; - exts = g_list_append(exts, &default_priority); - imquic_moq_object_extension default_order = { 0 }; - default_order.id = IMQUIC_MOQ_EXT_DEFAULT_GROUP_ORDER; - default_order.value.number = IMQUIC_MOQ_ORDERING_ASCENDING; - exts = g_list_append(exts, &default_order); - imquic_moq_accept_subscribe(conn, moq_request_id, moq_track_alias, &rparams, exts); - g_list_free(exts); + imquic_moq_accept_subscribe(conn, moq_request_id, moq_track_alias, &rparams, NULL); /* Start sending objects */ IMQUIC_LOG(IMQUIC_LOG_INFO, "[%s] -- Starting delivery of objects: [%"SCNu64"/%"SCNu64"] --> [%"SCNu64"/%"SCNu64"]\n", imquic_get_connection_name(conn), sub_start.group, sub_start.object, sub_end.group, sub_end.object); @@ -292,9 +257,10 @@ static void imquic_demo_incoming_unsubscribe(imquic_connection *conn, uint64_t r g_atomic_int_set(&send_objects, 0); } -static void imquic_demo_incoming_go_away(imquic_connection *conn, const char *uri) { +static void imquic_demo_incoming_go_away(imquic_connection *conn, const char *uri, uint64_t timeout) { /* Connection was closed */ - IMQUIC_LOG(IMQUIC_LOG_INFO, "[%s] Got a GOAWAY: %s\n", imquic_get_connection_name(conn), uri); + IMQUIC_LOG(IMQUIC_LOG_INFO, "[%s] Got a GOAWAY: %s (timeout=%"SCNu64"ms)\n", + imquic_get_connection_name(conn), uri, timeout); /* Stop here */ g_atomic_int_inc(&stop); } @@ -317,43 +283,44 @@ static void imquic_demo_connection_gone(imquic_connection *conn) { } static void imquic_demo_send_data(char *text, gboolean first, gboolean last) { - GList *exts = NULL; - imquic_moq_object_extension pgidext = { 0 }; - imquic_moq_object_extension poidext = { 0 }; - imquic_moq_object_extension numext = { 0 }; - imquic_moq_object_extension dataext = { 0 }; + GList *props = NULL; + imquic_moq_property pgidprop = { 0 }; + imquic_moq_property poidprop = { 0 }; + imquic_moq_property numprop = { 0 }; + imquic_moq_property dataprop = { 0 }; if((first && options.first_group > 0 && group_id == options.first_group) || (first && options.first_object > 0 && object_id == options.first_object) || - options.extensions) { - /* We have extensions to add to the object */ + options.properties) { + /* We have properties to add to the object */ if(first && options.first_group > 0 && group_id == options.first_group) { - /* Add the Prior Group ID Gap extension */ - pgidext.id = IMQUIC_MOQ_EXT_PRIOR_GROUP_ID_GAP; - pgidext.value.number = options.first_group; - exts = g_list_append(exts, &pgidext); + /* Add the Prior Group ID Gap property */ + pgidprop.id = IMQUIC_MOQ_PROPERTY_PRIOR_GROUP_ID_GAP; + pgidprop.value.number = options.first_group; + props = g_list_append(props, &pgidprop); } if(first && options.first_object > 0 && object_id == options.first_object) { - /* Add the Prior Object ID Gap extension */ - poidext.id = IMQUIC_MOQ_EXT_PRIOR_OBJECT_ID_GAP; - poidext.value.number = options.first_object; - exts = g_list_append(exts, &poidext); + /* Add the Prior Object ID Gap property */ + poidprop.id = IMQUIC_MOQ_PROPERTY_PRIOR_OBJECT_ID_GAP; + poidprop.value.number = options.first_object; + props = g_list_append(props, &poidprop); } - if(options.extensions) { - /* Just for fun, we add a couple of fake extensions to the object: a numeric - * extension set to the length of the text, and a data extension with a fixed string */ - numext.id = 0x6; /* FIXME */ - numext.value.number = strlen(text); - exts = g_list_append(exts, &numext); - dataext.id = 0x7; /* FIXME */ - dataext.value.data.buffer = (uint8_t *)"lminiero"; - dataext.value.data.length = strlen("lminiero"); - exts = g_list_append(exts, &dataext); + if(options.properties) { + /* Just for fun, we add a couple of fake properties to the object: a numeric + * property set to the length of the text, and a data property with a fixed string. + * We use the reserved space mentioned in the draft, for their IDs */ + numprop.id = 0x38; + numprop.value.number = strlen(text); + props = g_list_append(props, &numprop); + dataprop.id = 0x39; + dataprop.value.data.buffer = (uint8_t *)"lminiero"; + dataprop.value.data.length = strlen("lminiero"); + props = g_list_append(props, &dataprop); } } /* Check if it matches the filter */ if(group_id < sub_start.group || (group_id == sub_start.group && object_id < sub_start.object)) { /* Not the time to send the object yet */ - g_list_free(exts); + g_list_free(props); return; } if(group_id > sub_end.group || (group_id == sub_end.group && object_id > sub_end.object)) { @@ -365,7 +332,7 @@ static void imquic_demo_send_data(char *text, gboolean first, gboolean last) { g_atomic_int_set(&done_sent, 1); moq_request_id = 0; g_atomic_int_set(&send_objects, 0); - g_list_free(exts); + g_list_free(props); return; } else if(group_id == sub_end.group && object_id == sub_end.object) { last = TRUE; @@ -380,19 +347,19 @@ static void imquic_demo_send_data(char *text, gboolean first, gboolean last) { .object_status = 0, .payload = (uint8_t *)text, .payload_len = strlen(text), - .extensions = exts, + .properties = props, .delivery = delivery, .end_of_stream = FALSE }; imquic_moq_send_object(moq_conn, &object); - g_list_free(exts); + g_list_free(props); if(last && delivery == IMQUIC_MOQ_USE_SUBGROUP) { /* Send an empty object with status "end of X" */ object.object_id++; object.object_status = IMQUIC_MOQ_END_OF_GROUP; object.payload_len = 0; object.payload = NULL; - object.extensions = NULL; + object.properties = NULL; object.end_of_stream = TRUE; imquic_moq_send_object(moq_conn, &object); } @@ -453,11 +420,9 @@ int main(int argc, char *argv[]) { IMQUIC_LOG(IMQUIC_LOG_INFO, "Early data support enabled (ticket file '%s')\n", options.ticket_file); if(options.moq_version != NULL) { if(!strcasecmp(options.moq_version, "any")) { - IMQUIC_LOG(IMQUIC_LOG_INFO, "Negotiating version of MoQ between 11 and %d\n", IMQUIC_MOQ_VERSION_MAX - IMQUIC_MOQ_VERSION_BASE); + IMQUIC_LOG(IMQUIC_LOG_INFO, "Negotiating version of MoQ between %d and %d\n", + IMQUIC_MOQ_VERSION_MIN - IMQUIC_MOQ_VERSION_BASE, IMQUIC_MOQ_VERSION_MAX - IMQUIC_MOQ_VERSION_BASE); moq_version = IMQUIC_MOQ_VERSION_ANY; - } else if(!strcasecmp(options.moq_version, "legacy")) { - IMQUIC_LOG(IMQUIC_LOG_INFO, "Negotiating version of MoQ between 6 and 10\n"); - moq_version = IMQUIC_MOQ_VERSION_ANY_LEGACY; } else { moq_version = IMQUIC_MOQ_VERSION_BASE + atoi(options.moq_version); if(moq_version < IMQUIC_MOQ_VERSION_MIN || moq_version > IMQUIC_MOQ_VERSION_MAX) { @@ -490,18 +455,13 @@ int main(int argc, char *argv[]) { } } if(options.first_group > 0) - IMQUIC_LOG(IMQUIC_LOG_INFO, "First group: %"SCNu64" (will send the 'Prior Group ID Gap' extension)\n", options.first_group); + IMQUIC_LOG(IMQUIC_LOG_INFO, "First group: %"SCNu64" (will send the 'Prior Group ID Gap' property)\n", options.first_group); if(options.first_object > 0) - IMQUIC_LOG(IMQUIC_LOG_INFO, "First object: %"SCNu64" (will send the 'Prior Object ID Gap' extension)\n", options.first_object); + IMQUIC_LOG(IMQUIC_LOG_INFO, "First object: %"SCNu64" (will send the 'Prior Object ID Gap' property)\n", options.first_object); if(options.publish) { IMQUIC_LOG(IMQUIC_LOG_INFO, "Will use PUBLISH instead of PUBLISH_NAMESPACE + SUBSCRIBE\n"); - if(moq_version > IMQUIC_MOQ_VERSION_MIN && moq_version < IMQUIC_MOQ_VERSION_12) { - IMQUIC_LOG(IMQUIC_LOG_FATAL, "PUBLISH only supported starting from version 12\n"); - ret = 1; - goto done; - } } - IMQUIC_LOG(IMQUIC_LOG_INFO, "Will use track_alias=%"SCNu64", if a version higher or equal than 12 is negotiated\n", + IMQUIC_LOG(IMQUIC_LOG_INFO, "Will use track_alias=%"SCNu64"\n", options.track_alias); moq_track_alias = options.track_alias; @@ -652,18 +612,8 @@ int main(int argc, char *argv[]) { /* We're done, check if we need to send a PUBLISH_DONE and/or an PUBLISH_NAMESPACE_DONE */ if(g_atomic_int_get(&started) && !g_atomic_int_get(&done_sent)) imquic_moq_publish_done(moq_conn, moq_request_id, IMQUIC_MOQ_PUBDONE_SUBSCRIPTION_ENDED, "Publisher left"); - if(!options.publish) { - imquic_moq_namespace tns[32]; /* FIXME */ - int i = 0; - while(options.track_namespace[i] != NULL) { - const char *track_namespace = options.track_namespace[i]; - tns[i].buffer = (uint8_t *)track_namespace; - tns[i].length = strlen(track_namespace); - tns[i].next = (options.track_namespace[i+1] != NULL) ? &tns[i+1] : NULL; - i++; - } - imquic_moq_publish_namespace_done(moq_conn, &tns[0]); - } + if(!options.publish) + imquic_moq_publish_namespace_done(moq_conn, moq_tns_request_id); /* Shutdown the client */ imquic_shutdown_endpoint(client); diff --git a/examples/moq-relay-options.c b/examples/moq-relay-options.c index d841cb9..37f9e04 100644 --- a/examples/moq-relay-options.c +++ b/examples/moq-relay-options.c @@ -15,7 +15,7 @@ static GOptionContext *opts = NULL; gboolean demo_options_parse(demo_options *options, int argc, char *argv[]) { /* Supported command-line arguments */ GOptionEntry opt_entries[] = { - { "moq-draft-version", 'M', 0, G_OPTION_ARG_STRING, &options->moq_version, "MoQ draft version number to negotiate (default=any)", "|any|legacy" }, + { "moq-draft-version", 'M', 0, G_OPTION_ARG_STRING, &options->moq_version, "MoQ draft version number to negotiate (default=any)", "|any" }, { "auth-info", 'a', 0, G_OPTION_ARG_STRING, &options->auth_info, "Auth info required to connect to this relay, if any (default=none)", "string" }, { "subscribe-auth-info", 'A', 0, G_OPTION_ARG_STRING, &options->sub_auth_info, "Auth info required to subscribe to feeds in this relay, if any (default=none)", "string" }, { "bind", 'b', 0, G_OPTION_ARG_STRING, &options->ip, "Local IP address to bind to (default=all interfaces)", "IP" }, diff --git a/examples/moq-relay.c b/examples/moq-relay.c index 8f0e684..09d9fc0 100644 --- a/examples/moq-relay.c +++ b/examples/moq-relay.c @@ -49,6 +49,7 @@ static GList *fetches = NULL; typedef struct imquic_demo_moq_publisher { imquic_connection *conn; GHashTable *namespaces; + GHashTable *namespaces_by_id; GHashTable *subscriptions; GHashTable *subscriptions_by_id; uint64_t relay_track_alias; @@ -59,6 +60,7 @@ static void imquic_demo_moq_publisher_destroy(imquic_demo_moq_publisher *pub); typedef struct imquic_demo_moq_published_namespace { imquic_demo_moq_publisher *pub; + uint64_t request_id; char *track_namespace; imquic_moq_namespace *tns; gboolean announced; @@ -66,7 +68,7 @@ typedef struct imquic_demo_moq_published_namespace { imquic_mutex mutex; } imquic_demo_moq_published_namespace; static imquic_demo_moq_published_namespace *imquic_demo_moq_published_namespace_create(imquic_demo_moq_publisher *pub, - const char *track_namespace, imquic_moq_namespace *tns, gboolean announced); + uint64_t request_id, const char *track_namespace, imquic_moq_namespace *tns, gboolean announced); static void imquic_demo_moq_published_namespace_destroy(imquic_demo_moq_published_namespace *annc); typedef struct imquic_demo_moq_track { @@ -79,7 +81,7 @@ typedef struct imquic_demo_moq_track { gboolean pending; GList *subscriptions; GList *objects; - GList *extensions; + GList *properties; imquic_mutex mutex; } imquic_demo_moq_track; static imquic_demo_moq_track *imquic_demo_moq_track_create(imquic_demo_moq_published_namespace *annc, const char *track_name); @@ -135,6 +137,7 @@ static imquic_demo_moq_publisher *imquic_demo_moq_publisher_create(imquic_connec pub->conn = conn; pub->namespaces = g_hash_table_new_full(g_str_hash, g_str_equal, (GDestroyNotify)g_free, (GDestroyNotify)imquic_demo_moq_published_namespace_destroy); + pub->namespaces_by_id = g_hash_table_new_full(g_int64_hash, g_int64_equal, (GDestroyNotify)g_free, NULL); pub->subscriptions = g_hash_table_new_full(g_int64_hash, g_int64_equal, (GDestroyNotify)g_free, NULL); pub->subscriptions_by_id = g_hash_table_new_full(g_int64_hash, g_int64_equal, (GDestroyNotify)g_free, NULL); @@ -155,6 +158,8 @@ static void imquic_demo_moq_publisher_destroy(imquic_demo_moq_publisher *pub) { } g_hash_table_unref(pub->namespaces); } + if(pub->namespaces_by_id) + g_hash_table_unref(pub->namespaces_by_id); if(pub->subscriptions != NULL) g_hash_table_unref(pub->subscriptions); if(pub->subscriptions_by_id != NULL) @@ -164,9 +169,10 @@ static void imquic_demo_moq_publisher_destroy(imquic_demo_moq_publisher *pub) { } } static imquic_demo_moq_published_namespace *imquic_demo_moq_published_namespace_create(imquic_demo_moq_publisher *pub, - const char *track_namespace, imquic_moq_namespace *tns, gboolean announced) { + uint64_t request_id, const char *track_namespace, imquic_moq_namespace *tns, gboolean announced) { imquic_demo_moq_published_namespace *annc = g_malloc0(sizeof(imquic_demo_moq_published_namespace)); annc->pub = pub; + annc->request_id = request_id; annc->track_namespace = g_strdup(track_namespace); annc->tns = imquic_moq_namespace_duplicate(tns); annc->announced = announced; @@ -236,8 +242,8 @@ static void imquic_demo_moq_track_destroy(imquic_demo_moq_track *t) { t->subscriptions = NULL; g_list_free_full(t->objects, (GDestroyNotify)imquic_moq_object_cleanup); t->objects = NULL; - g_list_free_full(t->extensions, (GDestroyNotify)imquic_moq_object_extension_cleanup); - t->extensions = NULL; + g_list_free_full(t->properties, (GDestroyNotify)imquic_moq_property_cleanup); + t->properties = NULL; imquic_mutex_unlock(&t->mutex); imquic_mutex_destroy(&t->mutex); g_free(t); @@ -324,7 +330,7 @@ static void imquic_demo_moq_monitor_destroy(imquic_demo_moq_monitor *mon) { /* Helper functions to return monitors that match a namespace */ static GList *imquic_demo_match_monitors(imquic_connection *conn, imquic_moq_namespace *tns) { - if(monitors == NULL || tns == NULL) + if(monitors == NULL) return NULL; imquic_demo_moq_monitor *mon = NULL; GList *list = NULL, *temp = monitors; @@ -357,28 +363,16 @@ static void imquic_demo_alert_monitors(imquic_demo_moq_published_namespace *annc if(annc->announced && (mon->subscribe_options == IMQUIC_MOQ_WANT_NAMESPACE || mon->subscribe_options == IMQUIC_MOQ_WANT_PUBLISH_AND_NAMESPACE)) { /* The subscriber wants to be notified about new namespaces */ if(!done && !mon->published) { - /* Send a PUBLISH_NAMESPACE or a NAMESPACE, depending on the version */ + /* Send a NAMESPACE: we pass the full track namespace, + * the stack will automatically get the suffix for us */ mon->published = TRUE; - if(imquic_moq_get_version(mon->conn) <= IMQUIC_MOQ_VERSION_15) { - /* Use the legacy PUBLISH_NAMESPACE */ - uint64_t request_id = imquic_moq_get_next_request_id(mon->conn); - imquic_moq_publish_namespace(mon->conn, request_id, tns, NULL); - } else { - /* Use the new NAMESPACE: we pass the full track namespace, - * the stack will automatically get the suffix for us */ - imquic_moq_notify_namespace(mon->conn, mon->request_id, tns); - } + imquic_moq_notify_namespace(mon->conn, mon->request_id, tns); } else if(done && mon->published) { /* Send a PUBLISH_NAMESPACE_DONE or a NAMESPACE_DONE, depending on the version */ mon->published = FALSE; - if(imquic_moq_get_version(mon->conn) <= IMQUIC_MOQ_VERSION_15) { - /* Use the legacy PUBLISH_NAMESPACE */ - imquic_moq_publish_namespace_done(mon->conn, tns); - } else { - /* Use the new NAMESPACE_DONE: we pass the full track namespace, - * the stack will automatically get the suffix for us */ - imquic_moq_notify_namespace_done(mon->conn, mon->request_id, tns); - } + /* Send a NAMESPACE_DONE: we pass the full track namespace, + * the stack will automatically get the suffix for us */ + imquic_moq_notify_namespace_done(mon->conn, mon->request_id, tns); } } if(mon->subscribe_options != IMQUIC_MOQ_WANT_PUBLISH && mon->subscribe_options != IMQUIC_MOQ_WANT_PUBLISH_AND_NAMESPACE) { @@ -418,7 +412,7 @@ static void imquic_demo_alert_monitors(imquic_demo_moq_published_namespace *annc }; imquic_moq_request_parameters params; imquic_moq_request_parameters_init_defaults(¶ms); - imquic_moq_publish(mon->conn, relay_request_id, tns, &tn, relay_track_alias, ¶ms, track->extensions); + imquic_moq_publish(mon->conn, relay_request_id, 0, tns, &tn, relay_track_alias, ¶ms, track->properties); } temp = temp->next; } @@ -481,7 +475,7 @@ static void imquic_demo_ready(imquic_connection *conn) { peer ? peer : "unknown implementation"); } -static void imquic_demo_incoming_publish_namespace(imquic_connection *conn, uint64_t request_id, imquic_moq_namespace *tns, imquic_moq_request_parameters *parameters) { +static void imquic_demo_incoming_publish_namespace(imquic_connection *conn, uint64_t request_id, uint64_t required_id_delta, imquic_moq_namespace *tns, imquic_moq_request_parameters *parameters) { /* We received an publish_namespace */ char buffer[256]; const char *ns = imquic_moq_namespace_str(tns, buffer, sizeof(buffer), TRUE); @@ -504,8 +498,9 @@ static void imquic_demo_incoming_publish_namespace(imquic_connection *conn, uint g_hash_table_insert(publishers, conn, pub); } /* Let's keep track of it */ - imquic_demo_moq_published_namespace *annc = imquic_demo_moq_published_namespace_create(pub, ns, tns, TRUE); + imquic_demo_moq_published_namespace *annc = imquic_demo_moq_published_namespace_create(pub, request_id, ns, tns, TRUE); g_hash_table_insert(pub->namespaces, g_strdup(ns), annc); + g_hash_table_insert(pub->namespaces_by_id, imquic_uint64_dup(request_id), annc); g_hash_table_insert(namespaces, g_strdup(ns), annc); /* Check if there's monitors interested in this */ imquic_demo_alert_monitors(annc, NULL, FALSE); @@ -514,15 +509,16 @@ static void imquic_demo_incoming_publish_namespace(imquic_connection *conn, uint imquic_moq_accept_publish_namespace(conn, request_id, NULL); } -static void imquic_demo_incoming_publish_namespace_cancel(imquic_connection *conn, imquic_moq_namespace *tns, imquic_moq_request_error_code error_code, const char *reason) { +static void imquic_demo_incoming_publish_namespace_cancel(imquic_connection *conn, uint64_t request_id, imquic_moq_request_error_code error_code, const char *reason) { /* We received an publish_namespace cancel */ - char buffer[256]; - const char *ns = imquic_moq_namespace_str(tns, buffer, sizeof(buffer), TRUE); - IMQUIC_LOG(IMQUIC_LOG_INFO, "[%s] Cancelled publishing of namespace: '%s' (%d, %s)\n", - imquic_get_connection_name(conn), ns, error_code, reason); + IMQUIC_LOG(IMQUIC_LOG_INFO, "[%s] Cancelled publishing of namespace: %"SCNu64" (%d, %s)\n", + imquic_get_connection_name(conn), request_id, error_code, reason); /* Find the namespace */ imquic_mutex_lock(&mutex); - imquic_demo_moq_published_namespace *annc = g_hash_table_lookup(namespaces, ns); + imquic_demo_moq_published_namespace *annc = NULL; + imquic_demo_moq_publisher *pub = g_hash_table_lookup(publishers, conn); + if(pub != NULL) + annc = g_hash_table_lookup(pub->namespaces_by_id, &request_id); if(annc == NULL || !annc->announced) { imquic_mutex_unlock(&mutex); IMQUIC_LOG(IMQUIC_LOG_WARN, "[%s] Namespace not found\n", @@ -536,21 +532,22 @@ static void imquic_demo_incoming_publish_namespace_cancel(imquic_connection *con return; } /* Get rid of it */ - g_hash_table_remove(namespaces, ns); + g_hash_table_remove(namespaces, annc->track_namespace); if(annc->pub->namespaces) g_hash_table_remove(annc->pub->namespaces, annc->track_namespace); imquic_mutex_unlock(&mutex); } -static void imquic_demo_publish_namespace_done(imquic_connection *conn, imquic_moq_namespace *tns) { +static void imquic_demo_publish_namespace_done(imquic_connection *conn, uint64_t request_id) { /* We received an publish_namespace_done */ - char buffer[256]; - const char *ns = imquic_moq_namespace_str(tns, buffer, sizeof(buffer), TRUE); - IMQUIC_LOG(IMQUIC_LOG_INFO, "[%s] Publish Namespace done: '%s'\n", - imquic_get_connection_name(conn), ns); + IMQUIC_LOG(IMQUIC_LOG_INFO, "[%s] Publish Namespace done: %"SCNu64"\n", + imquic_get_connection_name(conn), request_id); /* Find the namespace */ imquic_mutex_lock(&mutex); - imquic_demo_moq_published_namespace *annc = g_hash_table_lookup(namespaces, ns); + imquic_demo_moq_published_namespace *annc = NULL; + imquic_demo_moq_publisher *pub = g_hash_table_lookup(publishers, conn); + if(pub != NULL) + annc = g_hash_table_lookup(pub->namespaces_by_id, &request_id); if(annc == NULL || !annc->announced) { imquic_mutex_unlock(&mutex); IMQUIC_LOG(IMQUIC_LOG_WARN, "[%s] Namespace not found\n", @@ -566,20 +563,20 @@ static void imquic_demo_publish_namespace_done(imquic_connection *conn, imquic_m /* Check if there's monitors interested in this */ imquic_demo_alert_monitors(annc, NULL, TRUE); /* Get rid of it */ - g_hash_table_remove(namespaces, ns); + g_hash_table_remove(namespaces, annc->track_namespace); if(annc->pub->namespaces) g_hash_table_remove(annc->pub->namespaces, annc->track_namespace); imquic_mutex_unlock(&mutex); } -static void imquic_demo_incoming_publish(imquic_connection *conn, uint64_t request_id, imquic_moq_namespace *tns, imquic_moq_name *tn, - uint64_t track_alias, imquic_moq_request_parameters *parameters, GList *track_extensions) { +static void imquic_demo_incoming_publish(imquic_connection *conn, uint64_t request_id, uint64_t required_id_delta, imquic_moq_namespace *tns, imquic_moq_name *tn, + uint64_t track_alias, imquic_moq_request_parameters *parameters, GList *track_properties) { /* We received a publish */ char tns_buffer[256], tn_buffer[256]; const char *ns = imquic_moq_namespace_str(tns, tns_buffer, sizeof(tns_buffer), TRUE); const char *name = imquic_moq_track_str(tn, tn_buffer, sizeof(tn_buffer)); - IMQUIC_LOG(IMQUIC_LOG_INFO, "[%s] Incoming publish for '%s--%s' (ID %"SCNu64"/%"SCNu64"; %d extensions)\n", - imquic_get_connection_name(conn), ns, name, request_id, track_alias, g_list_length(track_extensions)); + IMQUIC_LOG(IMQUIC_LOG_INFO, "[%s] Incoming publish for '%s--%s' (ID %"SCNu64"/%"SCNu64"; %d properties)\n", + imquic_get_connection_name(conn), ns, name, request_id, track_alias, g_list_length(track_properties)); if(parameters->auth_token_set) imquic_moq_print_auth_info(conn, parameters->auth_token, parameters->auth_token_len); if(name == NULL || strlen(name) == 0) @@ -598,7 +595,7 @@ static void imquic_demo_incoming_publish(imquic_connection *conn, uint64_t reque g_hash_table_insert(publishers, conn, pub); } /* Let's keep track of it */ - annc = imquic_demo_moq_published_namespace_create(pub, ns, tns, (imquic_moq_get_version(conn) < IMQUIC_MOQ_VERSION_16)); + annc = imquic_demo_moq_published_namespace_create(pub, 0, ns, tns, FALSE); g_hash_table_insert(pub->namespaces, g_strdup(ns), annc); g_hash_table_insert(namespaces, g_strdup(ns), annc); } @@ -615,7 +612,7 @@ static void imquic_demo_incoming_publish(imquic_connection *conn, uint64_t reque track->published = TRUE; track->pending = FALSE; track->track_alias_valid = TRUE; - track->extensions = imquic_moq_object_extensions_duplicate(track_extensions); + track->properties = imquic_moq_properties_duplicate(track_properties); g_hash_table_insert(annc->tracks, g_strdup(name), track); g_hash_table_insert(annc->pub->subscriptions_by_id, imquic_uint64_dup(track->request_id), track); g_hash_table_insert(annc->pub->subscriptions, imquic_uint64_dup(track->track_alias), track); @@ -775,8 +772,6 @@ static void imquic_demo_incoming_track_status(imquic_connection *conn, uint64_t } imquic_mutex_unlock(&track->mutex); imquic_mutex_unlock(&mutex); - /* TODO We should make the track_alias mapping persistent for this subscriber */ - uint64_t track_alias = track->track_alias; imquic_moq_request_parameters rparams; imquic_moq_request_parameters_init_defaults(&rparams); rparams.expires_set = TRUE; @@ -787,30 +782,24 @@ static void imquic_demo_incoming_track_status(imquic_connection *conn, uint64_t rparams.largest_object_set = TRUE; rparams.largest_object = start; } - imquic_moq_accept_track_status(conn, request_id, track_alias, &rparams); + imquic_moq_accept_track_status(conn, request_id, &rparams); } -static void imquic_demo_incoming_subscribe(imquic_connection *conn, uint64_t request_id, uint64_t track_alias, +static void imquic_demo_incoming_subscribe(imquic_connection *conn, uint64_t request_id, uint64_t required_id_delta, imquic_moq_namespace *tns, imquic_moq_name *tn, imquic_moq_request_parameters *parameters) { /* We received a subscribe */ char tns_buffer[256], tn_buffer[256]; const char *ns = imquic_moq_namespace_str(tns, tns_buffer, sizeof(tns_buffer), TRUE); const char *name = imquic_moq_track_str(tn, tn_buffer, sizeof(tn_buffer)); - if(imquic_moq_get_version(conn) < IMQUIC_MOQ_VERSION_12) { - /* Older versions of MoQ expect the track alias in the SUBSCRIBE */ - IMQUIC_LOG(IMQUIC_LOG_INFO, "[%s] Incoming subscribe for '%s--%s' (ID %"SCNu64"/%"SCNu64")\n", - imquic_get_connection_name(conn), ns, name, request_id, track_alias); - } else { - IMQUIC_LOG(IMQUIC_LOG_INFO, "[%s] Incoming subscribe for '%s--%s' (ID %"SCNu64")\n", - imquic_get_connection_name(conn), ns, name, request_id); - } + IMQUIC_LOG(IMQUIC_LOG_INFO, "[%s] Incoming subscribe for '%s--%s' (ID %"SCNu64")\n", + imquic_get_connection_name(conn), ns, name, request_id); if(parameters->auth_token_set) imquic_moq_print_auth_info(conn, parameters->auth_token, parameters->auth_token_len); if(!imquic_moq_check_auth_info(conn, options.sub_auth_info, parameters->auth_token_set ? parameters->auth_token : NULL, parameters->auth_token_set ? parameters->auth_token_len : 0)) { IMQUIC_LOG(IMQUIC_LOG_WARN, "[%s] Incorrect authorization info provided\n", imquic_get_connection_name(conn)); - imquic_moq_reject_subscribe(conn, request_id, IMQUIC_MOQ_REQERR_UNAUTHORIZED, "Unauthorized access", track_alias, 0); + imquic_moq_reject_subscribe(conn, request_id, IMQUIC_MOQ_REQERR_UNAUTHORIZED, "Unauthorized access", 0); return; } if(name == NULL || strlen(name) == 0) @@ -822,7 +811,7 @@ static void imquic_demo_incoming_subscribe(imquic_connection *conn, uint64_t req imquic_mutex_unlock(&mutex); IMQUIC_LOG(IMQUIC_LOG_WARN, "[%s] Namespace not found\n", imquic_get_connection_name(conn)); - imquic_moq_reject_subscribe(conn, request_id, IMQUIC_MOQ_REQERR_DOES_NOT_EXIST, "Namespace not found", track_alias, 0); + imquic_moq_reject_subscribe(conn, request_id, IMQUIC_MOQ_REQERR_DOES_NOT_EXIST, "Namespace not found", 0); return; } /* Do we know this track already? */ @@ -846,15 +835,12 @@ static void imquic_demo_incoming_subscribe(imquic_connection *conn, uint64_t req /* FIXME Should we return an error? */ imquic_mutex_unlock(&mutex); IMQUIC_LOG(IMQUIC_LOG_WARN, "Already subscribed with ID %"SCNu64"\n", request_id); - imquic_moq_reject_subscribe(conn, request_id, IMQUIC_MOQ_REQERR_DUPLICATE_SUBSCRIPTION, "Already subscribed", track_alias, 0); + imquic_moq_reject_subscribe(conn, request_id, IMQUIC_MOQ_REQERR_DUPLICATE_SUBSCRIPTION, "Already subscribed", 0); return; } - if(imquic_moq_get_version(conn) >= IMQUIC_MOQ_VERSION_12) { - /* Starting from v12, the publisher (in this case the relay, since - * track_alias is handled hop-by-hop) always chooses the track_alias */ - track_alias = sub->relay_track_alias; - sub->relay_track_alias++; - } + /* Generate a track_alias */ + uint64_t track_alias = sub->relay_track_alias; + sub->relay_track_alias++; /* Create a subscription to this track */ imquic_demo_moq_subscription *s = imquic_demo_moq_subscription_create(sub, track, request_id, track_alias); g_hash_table_insert(sub->subscriptions_by_id, imquic_uint64_dup(request_id), s); @@ -908,18 +894,12 @@ static void imquic_demo_incoming_subscribe(imquic_connection *conn, uint64_t req rparams.largest_object_set = TRUE; rparams.largest_object = s->sub_start; } - imquic_moq_accept_subscribe(conn, request_id, track_alias, &rparams, track->extensions); + imquic_moq_accept_subscribe(conn, request_id, track_alias, &rparams, track->properties); } /* If we just created a placeholder track, forward the subscribe to the publisher */ if(new_track) { track->request_id = imquic_moq_get_next_request_id(annc->pub->conn); g_hash_table_insert(annc->pub->subscriptions_by_id, imquic_uint64_dup(track->request_id), track); - if(imquic_moq_get_version(conn) < IMQUIC_MOQ_VERSION_12) { - track->track_alias = annc->pub->relay_track_alias; - track->track_alias_valid = TRUE; - annc->pub->relay_track_alias++; - g_hash_table_insert(annc->pub->subscriptions, imquic_uint64_dup(track->track_alias), track); - } /* We send a 'Largest Object' filter to the subscriber, we'll filter ourselves in case */ imquic_moq_request_parameters params; imquic_moq_request_parameters_init_defaults(¶ms); @@ -931,18 +911,21 @@ static void imquic_demo_incoming_subscribe(imquic_connection *conn, uint64_t req params.forward = TRUE; params.subscription_filter_set = TRUE; params.subscription_filter.type = IMQUIC_MOQ_FILTER_LARGEST_OBJECT; - imquic_moq_subscribe(annc->pub->conn, track->request_id, track->track_alias, tns, tn, ¶ms); + if(imquic_moq_subscribe(annc->pub->conn, track->request_id, 0, tns, tn, ¶ms) < 0) { + g_hash_table_remove(annc->pub->subscriptions_by_id, &track->request_id); + imquic_moq_reject_subscribe(conn, request_id, IMQUIC_MOQ_REQERR_INTERNAL_ERROR, "Error creating upstream subscription", 0); + } } imquic_mutex_unlock(&mutex); } static void imquic_demo_subscribe_accepted(imquic_connection *conn, uint64_t request_id, uint64_t track_alias, - imquic_moq_request_parameters *parameters, GList *track_extensions) { + imquic_moq_request_parameters *parameters, GList *track_properties) { /* Our subscription to a publisher was accepted */ - IMQUIC_LOG(IMQUIC_LOG_INFO, "[%s] Subscription %"SCNu64" accepted (expires=%"SCNu64"; %s order; %d extensions)\n", + IMQUIC_LOG(IMQUIC_LOG_INFO, "[%s] Subscription %"SCNu64" accepted (expires=%"SCNu64"; %s order; %d properties)\n", imquic_get_connection_name(conn), request_id, parameters->expires, imquic_moq_group_order_str(parameters->group_order), - g_list_length(track_extensions)); + g_list_length(track_properties)); if(parameters->largest_object_set) { IMQUIC_LOG(IMQUIC_LOG_INFO, "[%s] -- Largest Location: %"SCNu64"/%"SCNu64"\n", imquic_get_connection_name(conn), @@ -962,13 +945,11 @@ static void imquic_demo_subscribe_accepted(imquic_connection *conn, uint64_t req IMQUIC_LOG(IMQUIC_LOG_WARN, "No track found for that subscription\n"); return; } - if(imquic_moq_get_version(conn) >= IMQUIC_MOQ_VERSION_12) { - /* Starting from v12, the publisher always chooses the track_alias */ - track->track_alias = track_alias; - track->track_alias_valid = TRUE; - g_hash_table_insert(pub->subscriptions, imquic_uint64_dup(track->track_alias), track); - } - track->extensions = imquic_moq_object_extensions_duplicate(track_extensions); + /* Keep track of the track_alias chosen by the publisher */ + track->track_alias = track_alias; + track->track_alias_valid = TRUE; + g_hash_table_insert(pub->subscriptions, imquic_uint64_dup(track->track_alias), track); + track->properties = imquic_moq_properties_duplicate(track_properties); /* Send a SUBSCRIBE_OK to all subscribers */ imquic_mutex_lock(&track->mutex); GList *temp = track->subscriptions; @@ -976,7 +957,7 @@ static void imquic_demo_subscribe_accepted(imquic_connection *conn, uint64_t req imquic_demo_moq_subscription *s = (imquic_demo_moq_subscription *)temp->data; if(s && s->sub && s->sub->conn) { s->active = TRUE; - imquic_moq_accept_subscribe(s->sub->conn, s->request_id, s->track_alias, parameters, track->extensions); + imquic_moq_accept_subscribe(s->sub->conn, s->request_id, s->track_alias, parameters, track->properties); } temp = temp->next; } @@ -988,16 +969,10 @@ static void imquic_demo_subscribe_accepted(imquic_connection *conn, uint64_t req imquic_mutex_unlock(&mutex); } -static void imquic_demo_subscribe_error(imquic_connection *conn, uint64_t request_id, imquic_moq_request_error_code error_code, const char *reason, uint64_t track_alias, uint64_t retry_interval) { +static void imquic_demo_subscribe_error(imquic_connection *conn, uint64_t request_id, imquic_moq_request_error_code error_code, const char *reason, uint64_t retry_interval) { /* Our subscription to a publisher was rejected */ - if(imquic_moq_get_version(conn) < IMQUIC_MOQ_VERSION_12) { - /* Older versions of MoQ passed the track alias in the SUBSCRIBE */ - IMQUIC_LOG(IMQUIC_LOG_INFO, "[%s] Got an error subscribing to ID %"SCNu64"/%"SCNu64": error %d (%s)\n", - imquic_get_connection_name(conn), request_id, track_alias, error_code, reason); - } else { - IMQUIC_LOG(IMQUIC_LOG_INFO, "[%s] Got an error subscribing via ID %"SCNu64": error %d (%s)\n", - imquic_get_connection_name(conn), request_id, error_code, reason); - } + IMQUIC_LOG(IMQUIC_LOG_INFO, "[%s] Got an error subscribing via ID %"SCNu64": error %d (%s)\n", + imquic_get_connection_name(conn), request_id, error_code, reason); /* Find the track associated to this subscription */ imquic_mutex_lock(&mutex); imquic_demo_moq_publisher *pub = g_hash_table_lookup(publishers, conn); @@ -1018,7 +993,7 @@ static void imquic_demo_subscribe_error(imquic_connection *conn, uint64_t reques while(temp) { imquic_demo_moq_subscription *s = (imquic_demo_moq_subscription *)temp->data; if(s && s->sub && s->sub->conn) - imquic_moq_reject_subscribe(s->sub->conn, s->request_id, error_code, reason, s->track_alias, 0); + imquic_moq_reject_subscribe(s->sub->conn, s->request_id, error_code, reason, 0); temp = temp->next; } imquic_mutex_unlock(&track->mutex); @@ -1029,7 +1004,7 @@ static void imquic_demo_subscribe_error(imquic_connection *conn, uint64_t reques } static void imquic_demo_request_updated(imquic_connection *conn, uint64_t request_id, - uint64_t sub_request_id, imquic_moq_request_parameters *parameters) { + uint64_t sub_request_id, uint64_t required_id_delta, imquic_moq_request_parameters *parameters) { IMQUIC_LOG(IMQUIC_LOG_INFO, "[%s] Incoming update for request %"SCNu64"\n", imquic_get_connection_name(conn), request_id); /* Find the subscriber */ @@ -1044,8 +1019,7 @@ static void imquic_demo_request_updated(imquic_connection *conn, uint64_t reques return; } /* Update the subscription */ - imquic_demo_moq_subscription *s = g_hash_table_lookup(sub->subscriptions_by_id, - imquic_moq_get_version(conn) >= IMQUIC_MOQ_VERSION_14 ? &sub_request_id : &request_id); + imquic_demo_moq_subscription *s = g_hash_table_lookup(sub->subscriptions_by_id, &sub_request_id); if(s == NULL) { imquic_mutex_unlock(&mutex); IMQUIC_LOG(IMQUIC_LOG_WARN, "[%s] Subscriber not found\n", @@ -1124,7 +1098,7 @@ static void imquic_demo_incoming_unsubscribe(imquic_connection *conn, uint64_t r imquic_mutex_unlock(&mutex); } -static void imquic_demo_incoming_subscribe_namespace(imquic_connection *conn, uint64_t request_id, +static void imquic_demo_incoming_subscribe_namespace(imquic_connection *conn, uint64_t request_id, uint64_t required_id_delta, imquic_moq_namespace *tns, imquic_moq_subscribe_namespace_options subscribe_options, imquic_moq_request_parameters *parameters) { /* We received a subscribe for a namespace tuple */ char tns_buffer[256]; @@ -1159,26 +1133,17 @@ static void imquic_demo_incoming_subscribe_namespace(imquic_connection *conn, ui imquic_mutex_unlock(&mutex); } -static void imquic_demo_incoming_unsubscribe_namespace(imquic_connection *conn, uint64_t request_id, imquic_moq_namespace *tns) { +static void imquic_demo_incoming_unsubscribe_namespace(imquic_connection *conn, uint64_t request_id) { /* We received an unsubscribe */ - char tns_buffer[256]; - const char *ns = imquic_moq_namespace_str(tns, tns_buffer, sizeof(tns_buffer), TRUE); - uint32_t conn_moq_version = imquic_moq_get_version(conn); - if(conn_moq_version <= IMQUIC_MOQ_VERSION_14) { - IMQUIC_LOG(IMQUIC_LOG_INFO, "[%s] Incoming unsubscribe for namespace prefix '%s'\n", - imquic_get_connection_name(conn), ns); - } else { - IMQUIC_LOG(IMQUIC_LOG_INFO, "[%s] Incoming unsubscribe for namespace via ID %"SCNu64"\n", - imquic_get_connection_name(conn), request_id); - } + IMQUIC_LOG(IMQUIC_LOG_INFO, "[%s] Incoming unsubscribe for namespace via ID %"SCNu64"\n", + imquic_get_connection_name(conn), request_id); /* FIXME Get rid of the associated monitor */ imquic_demo_moq_monitor *mon = NULL; imquic_mutex_lock(&mutex); GList *temp = monitors; while(temp) { mon = (imquic_demo_moq_monitor *)temp->data; - if(conn == mon->conn && ((conn_moq_version <= IMQUIC_MOQ_VERSION_14 && !strcasecmp(ns, mon->ns)) || - (conn_moq_version >= IMQUIC_MOQ_VERSION_15 && request_id == mon->request_id))) { + if(conn == mon->conn && request_id == mon->request_id) { monitors = g_list_delete_link(monitors, temp); imquic_demo_moq_monitor_destroy(mon); break; @@ -1188,7 +1153,7 @@ static void imquic_demo_incoming_unsubscribe_namespace(imquic_connection *conn, imquic_mutex_unlock(&mutex); } -static void imquic_demo_incoming_standalone_fetch(imquic_connection *conn, uint64_t request_id, +static void imquic_demo_incoming_standalone_fetch(imquic_connection *conn, uint64_t request_id, uint64_t required_id_delta, imquic_moq_namespace *tns, imquic_moq_name *tn, imquic_moq_location_range *range, imquic_moq_request_parameters *parameters) { //~ gboolean descending, imquic_moq_location_range *range, uint8_t *auth, size_t authlen) { /* We received a standalone fetch */ @@ -1273,11 +1238,11 @@ static void imquic_demo_incoming_standalone_fetch(imquic_connection *conn, uint6 imquic_moq_request_parameters_init_defaults(&rparams); rparams.group_order_set = parameters->group_order_set; rparams.group_order = parameters->group_order; - imquic_moq_accept_fetch(conn, request_id, &largest, &rparams, track->extensions); + imquic_moq_accept_fetch(conn, request_id, &largest, &rparams, track->properties); imquic_mutex_unlock(&mutex); } -static void imquic_demo_incoming_joining_fetch(imquic_connection *conn, uint64_t request_id, uint64_t joining_request_id , +static void imquic_demo_incoming_joining_fetch(imquic_connection *conn, uint64_t request_id, uint64_t required_id_delta, uint64_t joining_request_id, gboolean absolute, uint64_t joining_start, imquic_moq_request_parameters *parameters) { /* We received a joining fetch */ IMQUIC_LOG(IMQUIC_LOG_INFO, "[%s] Incoming %s joining fetch for subscription %"SCNu64" (ID %"SCNu64"; start=%"SCNu64"; %s order)\n", @@ -1347,7 +1312,7 @@ static void imquic_demo_incoming_joining_fetch(imquic_connection *conn, uint64_t imquic_moq_request_parameters_init_defaults(&rparams); rparams.group_order_set = parameters->group_order_set; rparams.group_order = parameters->group_order; - imquic_moq_accept_fetch(conn, request_id, &largest, &rparams, track->extensions); + imquic_moq_accept_fetch(conn, request_id, &largest, &rparams, track->properties); imquic_mutex_unlock(&mutex); } @@ -1371,10 +1336,10 @@ static void imquic_demo_incoming_fetch_cancel(imquic_connection *conn, uint64_t static void imquic_demo_incoming_object(imquic_connection *conn, imquic_moq_object *object) { /* We received an object */ if(!options.quiet) { - IMQUIC_LOG(IMQUIC_LOG_INFO, "[%s] Incoming object: reqid=%"SCNu64", alias=%"SCNu64", group=%"SCNu64", subgroup=%"SCNu64", id=%"SCNu64", payload=%zu bytes, extensions=%d, delivery=%s, status=%s, eos=%d\n", + IMQUIC_LOG(IMQUIC_LOG_INFO, "[%s] Incoming object: reqid=%"SCNu64", alias=%"SCNu64", group=%"SCNu64", subgroup=%"SCNu64", id=%"SCNu64", payload=%zu bytes, properties=%d, delivery=%s, status=%s, eos=%d\n", imquic_get_connection_name(conn), object->request_id, object->track_alias, object->group_id, object->subgroup_id, object->object_id, - object->payload_len, g_list_length(object->extensions), imquic_moq_delivery_str(object->delivery), + object->payload_len, g_list_length(object->properties), imquic_moq_delivery_str(object->delivery), imquic_moq_object_status_str(object->object_status), object->end_of_stream); } /* Find the track associated to this subscription */ @@ -1519,11 +1484,9 @@ int main(int argc, char *argv[]) { if(options.moq_version != NULL) { if(!strcasecmp(options.moq_version, "any")) { - IMQUIC_LOG(IMQUIC_LOG_INFO, "Negotiating version of MoQ between 11 and %d\n", IMQUIC_MOQ_VERSION_MAX - IMQUIC_MOQ_VERSION_BASE); + IMQUIC_LOG(IMQUIC_LOG_INFO, "Negotiating version of MoQ between %d and %d\n", + IMQUIC_MOQ_VERSION_MIN - IMQUIC_MOQ_VERSION_BASE, IMQUIC_MOQ_VERSION_MAX - IMQUIC_MOQ_VERSION_BASE); moq_version = IMQUIC_MOQ_VERSION_ANY; - } else if(!strcasecmp(options.moq_version, "legacy")) { - IMQUIC_LOG(IMQUIC_LOG_INFO, "Negotiating version of MoQ between 6 and 10\n"); - moq_version = IMQUIC_MOQ_VERSION_ANY_LEGACY; } else { moq_version = IMQUIC_MOQ_VERSION_BASE + atoi(options.moq_version); if(moq_version < IMQUIC_MOQ_VERSION_MIN || moq_version > IMQUIC_MOQ_VERSION_MAX) { diff --git a/examples/moq-sub-options.c b/examples/moq-sub-options.c index 0b8ce70..4ce618a 100644 --- a/examples/moq-sub-options.c +++ b/examples/moq-sub-options.c @@ -15,7 +15,7 @@ static GOptionContext *opts = NULL; gboolean demo_options_parse(demo_options *options, int argc, char *argv[]) { /* Supported command-line arguments */ GOptionEntry opt_entries[] = { - { "moq-draft-version", 'M', 0, G_OPTION_ARG_STRING, &options->moq_version, "MoQ draft version number to negotiate (default=any)", "|any|legacy" }, + { "moq-draft-version", 'M', 0, G_OPTION_ARG_STRING, &options->moq_version, "MoQ draft version number to negotiate (default=any)", "|any" }, { "track-namespace", 'n', 0, G_OPTION_ARG_STRING_ARRAY, &options->track_namespace, "MoQ track namespace to subscribe to (can be called multiple times to create a tuple; default=none)", "namespace" }, { "track-name", 'N', 0, G_OPTION_ARG_STRING_ARRAY, &options->track_name, "MoQ track name to subscribe to (can be called multiple times to subscribe to multiple tracks; default=none)", "name" }, { "relay-auth-info", 'a', 0, G_OPTION_ARG_STRING, &options->relay_auth_info, "Auth info required to connect to the relay, if any (default=none)", "string" }, diff --git a/examples/moq-sub.c b/examples/moq-sub.c index 3bb16f1..0cdcf7f 100644 --- a/examples/moq-sub.c +++ b/examples/moq-sub.c @@ -101,17 +101,17 @@ static const char *imquic_demo_media_type_str(imquic_demo_media_type type) { return NULL; } -typedef enum imquic_demo_loc_extension { - DEMO_LOC_MEDIA_TYPE = 0x0A, /* Media type header extension */ +typedef enum imquic_demo_loc_property { + DEMO_LOC_MEDIA_TYPE = 0x0A, /* Media type header property */ DEMO_LOC_H264_HEADER = 0x0B, /* Video H264 in AVCC metadata (TODO change to 0x15) */ DEMO_LOC_H264_EXTRADATA = 0x0D, /* Video H264 in AVCC extradata */ DEMO_LOC_OPUS_HEADER = 0x0F, /* Audio Opus bitstream data */ DEMO_LOC_AAC_HEADER = 0x13, /* Audio AAC-LC in MPEG4 bitstream data */ -} imquic_demo_loc_extension; -static const char *imquic_demo_loc_extension_str(imquic_demo_loc_extension type) { +} imquic_demo_loc_property; +static const char *imquic_demo_loc_property_str(imquic_demo_loc_property type) { switch(type) { case DEMO_LOC_MEDIA_TYPE: - return "Media type header extension"; + return "Media type property"; case DEMO_LOC_H264_HEADER: return "Video H264 in AVCC metadata"; case DEMO_LOC_H264_EXTRADATA: @@ -166,7 +166,6 @@ static void imquic_demo_ready(imquic_connection *conn) { /* Let's subscribe to the provided namespace/name(s) */ int i = 0; uint64_t request_id = 0; - uint64_t track_alias = 0; imquic_moq_namespace tns[32]; /* FIXME */ while(options.track_namespace[i] != NULL) { const char *track_namespace = options.track_namespace[i]; @@ -193,23 +192,11 @@ static void imquic_demo_ready(imquic_connection *conn) { if(options.subscribe_namespace) { /* Only send a SUBSCRIBE_NAMESPACE: the relay will send us a * PUBLISH request when there's something we can subscribe to */ - if(moq_version < IMQUIC_MOQ_VERSION_12) { - /* Version is too old, we can't: stop here */ - IMQUIC_LOG(IMQUIC_LOG_FATAL, "PUBLISH only supported starting from version 12\n"); - g_atomic_int_inc(&stop); - return; - } - /* Check if we need to request forwarding right away, or if we'll ask send an update later */ params.forward_set = TRUE; params.forward = TRUE; - if(options.update_subscribe > 0 && imquic_moq_get_version(conn) >= IMQUIC_MOQ_VERSION_11 && (options.fetch == NULL || options.join_offset >= 0)) + if(options.update_subscribe > 0 && (options.fetch == NULL || options.join_offset >= 0)) params.forward = FALSE; - imquic_moq_subscribe_namespace(conn, imquic_moq_get_next_request_id(conn), tns, IMQUIC_MOQ_WANT_PUBLISH_AND_NAMESPACE, ¶ms); - return; - } else if(options.track_status && moq_version < IMQUIC_MOQ_VERSION_13) { - /* Version is too old, we can't: stop here */ - IMQUIC_LOG(IMQUIC_LOG_FATAL, "TRACK_STATUS only supported starting from version 13\n"); - g_atomic_int_inc(&stop); + imquic_moq_subscribe_namespace(conn, imquic_moq_get_next_request_id(conn), 0, tns, IMQUIC_MOQ_WANT_PUBLISH_AND_NAMESPACE, ¶ms); return; } /* Parameters in case we need to FETCH */ @@ -223,7 +210,7 @@ static void imquic_demo_ready(imquic_connection *conn) { /* Check if we need to request forwarding right away, or if we'll ask send an update later */ params.forward_set = TRUE; params.forward = TRUE; - if(options.update_subscribe > 0 && imquic_moq_get_version(conn) >= IMQUIC_MOQ_VERSION_11 && (options.fetch == NULL || options.join_offset >= 0)) + if(options.update_subscribe > 0 && (options.fetch == NULL || options.join_offset >= 0)) params.forward = FALSE; params.subscriber_priority_set = TRUE; params.subscriber_priority = 128; @@ -239,18 +226,10 @@ static void imquic_demo_ready(imquic_connection *conn) { while(options.track_name[i] != NULL) { request_id = imquic_moq_get_next_request_id(conn); const char *track_name = options.track_name[i]; - if(imquic_moq_get_version(conn) < IMQUIC_MOQ_VERSION_12) { - /* Older versions of MoQ passed the track alias in the SUBSCRIBE */ - IMQUIC_LOG(IMQUIC_LOG_INFO, "[%s] %s to '%s--%s' (%s), using ID %"SCNu64"/%"SCNu64"\n", - imquic_get_connection_name(conn), - ((options.fetch != NULL && options.join_offset < 0) ? "Fetching" : "Subscribing"), - ns, track_name, imquic_demo_payload_type_str(payload_type), request_id, track_alias); - } else { - IMQUIC_LOG(IMQUIC_LOG_INFO, "[%s] %s to '%s--%s' (%s), using ID %"SCNu64"\n", - imquic_get_connection_name(conn), - ((options.fetch != NULL && options.join_offset < 0) ? "Fetching" : "Subscribing"), - ns, track_name, imquic_demo_payload_type_str(payload_type), request_id); - } + IMQUIC_LOG(IMQUIC_LOG_INFO, "[%s] %s to '%s--%s' (%s), using ID %"SCNu64"\n", + imquic_get_connection_name(conn), + ((options.fetch != NULL && options.join_offset < 0) ? "Fetching" : "Subscribing"), + ns, track_name, imquic_demo_payload_type_str(payload_type), request_id); imquic_moq_name tn = { .buffer = (uint8_t *)track_name, .length = strlen(track_name) @@ -258,7 +237,7 @@ static void imquic_demo_ready(imquic_connection *conn) { if(options.fetch == NULL) { if(!options.track_status) { /* Send a SUBSCRIBE */ - imquic_moq_subscribe(conn, request_id, track_alias, &tns[0], &tn, ¶ms); + imquic_moq_subscribe(conn, request_id, 0, &tns[0], &tn, ¶ms); if(!params.forward) request_ids = g_list_append(request_ids, imquic_uint64_dup(request_id)); } else { @@ -273,16 +252,15 @@ static void imquic_demo_ready(imquic_connection *conn) { .start = start_location, .end = end_location }; - imquic_moq_standalone_fetch(conn, request_id, &tns[0], &tn, &range, &fparams); + imquic_moq_standalone_fetch(conn, request_id, 0, &tns[0], &tn, &range, &fparams); } else { /* Send a SUBSCRIBE first, we'll send the joining FETCH when the subscription is accepted */ - imquic_moq_subscribe(conn, request_id, track_alias, &tns[0], &tn, ¶ms); + imquic_moq_subscribe(conn, request_id, 0, &tns[0], &tn, ¶ms); if(!params.forward) request_ids = g_list_append(request_ids, imquic_uint64_dup(request_id)); } } i++; - track_alias++; } if(!params.forward) { IMQUIC_LOG(IMQUIC_LOG_INFO, "[%s] Scheduling a REQUEST_UPDATE in %d seconds\n", @@ -291,22 +269,21 @@ static void imquic_demo_ready(imquic_connection *conn) { } } -static void imquic_demo_incoming_publish_namespace(imquic_connection *conn, uint64_t request_id, imquic_moq_namespace *tns, imquic_moq_request_parameters *parameters) { +static void imquic_demo_incoming_publish_namespace(imquic_connection *conn, uint64_t request_id, uint64_t required_id_delta, + imquic_moq_namespace *tns, imquic_moq_request_parameters *parameters) { /* We received an PUBLISH_NAMESPACE (older MoQ version) */ char buffer[256]; const char *ns = imquic_moq_namespace_str(tns, buffer, sizeof(buffer), TRUE); - IMQUIC_LOG(IMQUIC_LOG_INFO, "[%s] New published namespace: '%s'\n", - imquic_get_connection_name(conn), ns); + IMQUIC_LOG(IMQUIC_LOG_INFO, "[%s] New published namespace via ID %"SCNu64": '%s'\n", + imquic_get_connection_name(conn), request_id, ns); /* Accept the request */ imquic_moq_accept_publish_namespace(conn, request_id, NULL); } -static void imquic_demo_publish_namespace_done(imquic_connection *conn, imquic_moq_namespace *tns) { +static void imquic_demo_publish_namespace_done(imquic_connection *conn, uint64_t request_id) { /* We received an PUBLISH_NAMESPACE_DONE (older MoQ version) */ - char buffer[256]; - const char *ns = imquic_moq_namespace_str(tns, buffer, sizeof(buffer), TRUE); - IMQUIC_LOG(IMQUIC_LOG_INFO, "[%s] Publish Namespace done: '%s'\n", - imquic_get_connection_name(conn), ns); + IMQUIC_LOG(IMQUIC_LOG_INFO, "[%s] Publish Namespace done: %"SCNu64"\n", + imquic_get_connection_name(conn), request_id); } static void imquic_demo_incoming_namespace(imquic_connection *conn, uint64_t request_id, imquic_moq_namespace *tns_suffix) { @@ -325,14 +302,10 @@ static void imquic_demo_incoming_namespace_done(imquic_connection *conn, uint64_ imquic_get_connection_name(conn), ns); } -static void imquic_demo_track_status_accepted(imquic_connection *conn, uint64_t request_id, uint64_t track_alias, imquic_moq_request_parameters *parameters) { +static void imquic_demo_track_status_accepted(imquic_connection *conn, uint64_t request_id, imquic_moq_request_parameters *parameters) { IMQUIC_LOG(IMQUIC_LOG_INFO, "[%s] Track status %"SCNu64" accepted (expires=%"SCNu64"; %s order)\n", imquic_get_connection_name(conn), request_id, parameters->expires, imquic_moq_group_order_str(parameters->group_order)); - if(imquic_moq_get_version(conn) <= IMQUIC_MOQ_VERSION_14) { - IMQUIC_LOG(IMQUIC_LOG_INFO, "[%s] -- Track Alias: %"SCNu64"\n", - imquic_get_connection_name(conn), track_alias); - } if(parameters->largest_object_set) { IMQUIC_LOG(IMQUIC_LOG_INFO, "[%s] -- Largest Location: %"SCNu64"/%"SCNu64"\n", imquic_get_connection_name(conn), @@ -350,36 +323,20 @@ static void imquic_demo_track_status_error(imquic_connection *conn, uint64_t req } static void imquic_demo_subscribe_accepted(imquic_connection *conn, uint64_t request_id, uint64_t track_alias, - imquic_moq_request_parameters *parameters, GList *track_extensions) { - IMQUIC_LOG(IMQUIC_LOG_INFO, "[%s] Subscription %"SCNu64" accepted (expires=%"SCNu64"; %s order, %d extensions)\n", + imquic_moq_request_parameters *parameters, GList *track_properties) { + IMQUIC_LOG(IMQUIC_LOG_INFO, "[%s] Subscription %"SCNu64" accepted (expires=%"SCNu64"; %s order, %d properties)\n", imquic_get_connection_name(conn), request_id, parameters->expires, imquic_moq_group_order_str(parameters->group_order), - g_list_length(track_extensions)); - if(imquic_moq_get_version(conn) >= IMQUIC_MOQ_VERSION_12) { - /* Starting from v12, the publisher always chooses the track_alias */ - IMQUIC_LOG(IMQUIC_LOG_INFO, "[%s] -- Track Alias: %"SCNu64"\n", - imquic_get_connection_name(conn), track_alias); - } + g_list_length(track_properties)); + IMQUIC_LOG(IMQUIC_LOG_INFO, "[%s] -- Track Alias: %"SCNu64"\n", + imquic_get_connection_name(conn), track_alias); if(parameters->largest_object_set) { IMQUIC_LOG(IMQUIC_LOG_INFO, "[%s] -- Largest Location: %"SCNu64"/%"SCNu64"\n", imquic_get_connection_name(conn), parameters->largest_object.group, parameters->largest_object.object); } - if(track_extensions != NULL) { - GList *temp = track_extensions; - while(temp) { - imquic_moq_object_extension *ext = (imquic_moq_object_extension *)temp->data; - const char *ext_name = imquic_moq_extension_type_str(ext->id); - if(ext->id % 2 == 0) { - IMQUIC_LOG(IMQUIC_LOG_INFO, " >> Extension '%"SCNu32"' (%s) = %"SCNu64"\n", - ext->id, (ext_name ? ext_name : "unknown"), ext->value.number); - } else { - IMQUIC_LOG(IMQUIC_LOG_INFO, " >> Extension '%"SCNu32"' (%s) = %.*s\n", - ext->id, (ext_name ? ext_name : "unknown"), (int)ext->value.data.length, ext->value.data.buffer); - } - temp = temp->next; - } - } + if(track_properties != NULL) + imquic_moq_properties_print(track_properties); if(options.fetch != NULL && options.join_offset >= 0) { /* Send a Joining Fetch referencing this subscription */ imquic_moq_request_parameters fparams; @@ -401,20 +358,14 @@ static void imquic_demo_subscribe_accepted(imquic_connection *conn, uint64_t req uint64_t fetch_request_id = imquic_moq_get_next_request_id(conn); IMQUIC_LOG(IMQUIC_LOG_INFO, "[%s] Sending Joining Fetch for subscription %"SCNu64", using ID %"SCNu64" (offset=%d)\n", imquic_get_connection_name(conn), request_id, fetch_request_id, options.join_offset); - imquic_moq_joining_fetch(conn, fetch_request_id, request_id, + imquic_moq_joining_fetch(conn, fetch_request_id, 0, request_id, FALSE, options.join_offset, &fparams); } } -static void imquic_demo_subscribe_error(imquic_connection *conn, uint64_t request_id, imquic_moq_request_error_code error_code, const char *reason, uint64_t track_alias, uint64_t retry_interval) { - if(imquic_moq_get_version(conn) < IMQUIC_MOQ_VERSION_12) { - /* Older versions of MoQ passed the track alias in the SUBSCRIBE */ - IMQUIC_LOG(IMQUIC_LOG_INFO, "[%s] Got an error subscribing to ID %"SCNu64"/%"SCNu64": error %d (%s)\n", - imquic_get_connection_name(conn), request_id, track_alias, error_code, reason); - } else { - IMQUIC_LOG(IMQUIC_LOG_INFO, "[%s] Got an error subscribing via ID %"SCNu64": error %d (%s)\n", - imquic_get_connection_name(conn), request_id, error_code, reason); - } +static void imquic_demo_subscribe_error(imquic_connection *conn, uint64_t request_id, imquic_moq_request_error_code error_code, const char *reason, uint64_t retry_interval) { + IMQUIC_LOG(IMQUIC_LOG_INFO, "[%s] Got an error subscribing via ID %"SCNu64": error %d (%s)\n", + imquic_get_connection_name(conn), request_id, error_code, reason); /* Stop here */ g_atomic_int_inc(&stop); } @@ -429,33 +380,20 @@ static void imquic_demo_request_update_error(imquic_connection *conn, uint64_t r imquic_get_connection_name(conn), request_id, error_code, reason); } -static void imquic_demo_incoming_publish(imquic_connection *conn, uint64_t request_id, imquic_moq_namespace *tns, imquic_moq_name *tn, - uint64_t track_alias, imquic_moq_request_parameters *parameters, GList *track_extensions) { +static void imquic_demo_incoming_publish(imquic_connection *conn, uint64_t request_id, uint64_t required_id_delta, + imquic_moq_namespace *tns, imquic_moq_name *tn, uint64_t track_alias, imquic_moq_request_parameters *parameters, GList *track_properties) { /* We received a publish */ char tns_buffer[256], tn_buffer[256]; const char *ns = imquic_moq_namespace_str(tns, tns_buffer, sizeof(tns_buffer), TRUE); const char *name = imquic_moq_track_str(tn, tn_buffer, sizeof(tn_buffer)); - IMQUIC_LOG(IMQUIC_LOG_INFO, "[%s] Incoming publish for '%s--%s' (ID %"SCNu64"/%"SCNu64"; %d extensions)\n", - imquic_get_connection_name(conn), ns, name, request_id, track_alias, g_list_length(track_extensions)); + IMQUIC_LOG(IMQUIC_LOG_INFO, "[%s] Incoming publish for '%s--%s' (ID %"SCNu64"/%"SCNu64"; %d properties)\n", + imquic_get_connection_name(conn), ns, name, request_id, track_alias, g_list_length(track_properties)); if(parameters->auth_token_set) imquic_moq_print_auth_info(conn, parameters->auth_token, parameters->auth_token_len); if(name == NULL || strlen(name) == 0) name = "temp"; - if(track_extensions != NULL) { - GList *temp = track_extensions; - while(temp) { - imquic_moq_object_extension *ext = (imquic_moq_object_extension *)temp->data; - const char *ext_name = imquic_moq_extension_type_str(ext->id); - if(ext->id % 2 == 0) { - IMQUIC_LOG(IMQUIC_LOG_INFO, " >> Extension '%"SCNu32"' (%s) = %"SCNu64"\n", - ext->id, (ext_name ? ext_name : "unknown"), ext->value.number); - } else { - IMQUIC_LOG(IMQUIC_LOG_INFO, " >> Extension '%"SCNu32"' (%s) = %.*s\n", - ext->id, (ext_name ? ext_name : "unknown"), (int)ext->value.data.length, ext->value.data.buffer); - } - temp = temp->next; - } - } + if(track_properties != NULL) + imquic_moq_properties_print(track_properties); /* Done */ imquic_moq_request_parameters rparams; imquic_moq_request_parameters_init_defaults(&rparams); @@ -469,7 +407,7 @@ static void imquic_demo_incoming_publish(imquic_connection *conn, uint64_t reque rparams.subscription_filter.type = filter_type; rparams.subscription_filter.start_location = start_location; rparams.subscription_filter.end_group = end_location_sub.group; - if(options.update_subscribe > 0 && imquic_moq_get_version(conn) >= IMQUIC_MOQ_VERSION_11 && (options.fetch == NULL || options.join_offset >= 0)) { + if(options.update_subscribe > 0 && (options.fetch == NULL || options.join_offset >= 0)) { rparams.forward = FALSE; IMQUIC_LOG(IMQUIC_LOG_INFO, "[%s] Scheduling a REQUEST_UPDATE in %d seconds\n", imquic_get_connection_name(conn), options.update_subscribe); @@ -488,26 +426,13 @@ static void imquic_demo_publish_done(imquic_connection *conn, uint64_t request_i } static void imquic_demo_fetch_accepted(imquic_connection *conn, uint64_t request_id, imquic_moq_location *largest, - imquic_moq_request_parameters *parameters, GList *track_extensions) { - IMQUIC_LOG(IMQUIC_LOG_INFO, "[%s] Fetch %"SCNu64" accepted (%s order; largest group/object %"SCNu64"/%"SCNu64"; %d extensions)\n", + imquic_moq_request_parameters *parameters, GList *track_properties) { + IMQUIC_LOG(IMQUIC_LOG_INFO, "[%s] Fetch %"SCNu64" accepted (%s order; largest group/object %"SCNu64"/%"SCNu64"; %d properties)\n", imquic_get_connection_name(conn), request_id, imquic_moq_group_order_str(parameters->group_order), - largest->group, largest->object, g_list_length(track_extensions)); - if(track_extensions != NULL) { - GList *temp = track_extensions; - while(temp) { - imquic_moq_object_extension *ext = (imquic_moq_object_extension *)temp->data; - const char *ext_name = imquic_moq_extension_type_str(ext->id); - if(ext->id % 2 == 0) { - IMQUIC_LOG(IMQUIC_LOG_INFO, " >> Extension '%"SCNu32"' (%s) = %"SCNu64"\n", - ext->id, (ext_name ? ext_name : "unknown"), ext->value.number); - } else { - IMQUIC_LOG(IMQUIC_LOG_INFO, " >> Extension '%"SCNu32"' (%s) = %.*s\n", - ext->id, (ext_name ? ext_name : "unknown"), (int)ext->value.data.length, ext->value.data.buffer); - } - temp = temp->next; - } - } + largest->group, largest->object, g_list_length(track_properties)); + if(track_properties != NULL) + imquic_moq_properties_print(track_properties); } static void imquic_demo_fetch_error(imquic_connection *conn, uint64_t request_id, imquic_moq_request_error_code error_code, const char *reason, uint64_t retry_interval) { @@ -532,10 +457,10 @@ static void imquic_demo_subscribe_namespace_error(imquic_connection *conn, uint6 static void imquic_demo_incoming_object(imquic_connection *conn, imquic_moq_object *object) { /* We received an object */ - IMQUIC_LOG(IMQUIC_LOG_INFO, "[%s] Incoming object: reqid=%"SCNu64", alias=%"SCNu64", group=%"SCNu64", subgroup=%"SCNu64", id=%"SCNu64", payload=%zu bytes, extensions=%d, delivery=%s, status=%s, eos=%d\n", + IMQUIC_LOG(IMQUIC_LOG_INFO, "[%s] Incoming object: reqid=%"SCNu64", alias=%"SCNu64", group=%"SCNu64", subgroup=%"SCNu64", id=%"SCNu64", payload=%zu bytes, properties=%d, delivery=%s, status=%s, eos=%d\n", imquic_get_connection_name(conn), object->request_id, object->track_alias, object->group_id, object->subgroup_id, object->object_id, - object->payload_len, g_list_length(object->extensions), imquic_moq_delivery_str(object->delivery), + object->payload_len, g_list_length(object->properties), imquic_moq_delivery_str(object->delivery), imquic_moq_object_status_str(object->object_status), object->end_of_stream); if(object->payload == NULL || object->payload_len == 0) { if(object->end_of_stream) { @@ -548,21 +473,8 @@ static void imquic_demo_incoming_object(imquic_connection *conn, imquic_moq_obje } return; } - if(object->extensions != NULL) { - GList *temp = object->extensions; - while(payload_type != DEMO_TYPE_LOC && temp) { - imquic_moq_object_extension *ext = (imquic_moq_object_extension *)temp->data; - const char *ext_name = imquic_moq_extension_type_str(ext->id); - if(ext->id % 2 == 0) { - IMQUIC_LOG(IMQUIC_LOG_INFO, " >> Extension '%"SCNu32"' (%s) = %"SCNu64"\n", - ext->id, (ext_name ? ext_name : "unknown"), ext->value.number); - } else { - IMQUIC_LOG(IMQUIC_LOG_INFO, " >> Extension '%"SCNu32"' (%s) = %.*s\n", - ext->id, (ext_name ? ext_name : "unknown"), (int)ext->value.data.length, ext->value.data.buffer); - } - temp = temp->next; - } - } + if(object->properties != NULL) + imquic_moq_properties_print(object->properties); if(file != NULL) fwrite(object->payload, 1, object->payload_len, file); if(payload_type == DEMO_TYPE_TEXT) { @@ -575,54 +487,54 @@ static void imquic_demo_incoming_object(imquic_connection *conn, imquic_moq_obje } else if(payload_type == DEMO_TYPE_LOC) { /* FIXME Assuming LOC from https://github.com/facebookexperimental/moq-encoder-player/ * which uses the MoQ-MI draft: https://datatracker.ietf.org/doc/html/draft-cenzano-moq-media-interop */ - if(object->extensions == NULL) { - IMQUIC_LOG(IMQUIC_LOG_WARN, " -- No extensions, missing LOC info?\n"); + if(object->properties == NULL) { + IMQUIC_LOG(IMQUIC_LOG_WARN, " -- No properties, missing LOC info?\n"); } else { - /* Parse the extensions to get access to the LOC info */ - IMQUIC_LOG(IMQUIC_LOG_INFO, " -- %d extensions\n", g_list_length(object->extensions)); + /* Parse the properties to get access to the LOC info */ + IMQUIC_LOG(IMQUIC_LOG_INFO, " -- %d properties\n", g_list_length(object->properties)); imquic_demo_media_type media_type = DEMO_MEDIA_NONE; - struct imquic_moq_object_extension_data *loc_header = NULL, *loc_extradata = NULL; - GList *temp = object->extensions; + struct imquic_moq_property_data *loc_header = NULL, *loc_extradata = NULL; + GList *temp = object->properties; while(temp) { - imquic_moq_object_extension *ext = (imquic_moq_object_extension *)temp->data; - switch(ext->id) { + imquic_moq_property *prop = (imquic_moq_property *)temp->data; + switch(prop->id) { case DEMO_LOC_MEDIA_TYPE: { - media_type = ext->value.number; + media_type = prop->value.number; IMQUIC_LOG(IMQUIC_LOG_INFO, " -- -- %s: %s\n", - imquic_demo_loc_extension_str(ext->id), + imquic_demo_loc_property_str(prop->id), imquic_demo_media_type_str(media_type)); break; } case DEMO_LOC_H264_HEADER: { - loc_header = &ext->value.data; + loc_header = &prop->value.data; IMQUIC_LOG(IMQUIC_LOG_INFO, " -- -- %s: %zu bytes\n", - imquic_demo_loc_extension_str(ext->id), + imquic_demo_loc_property_str(prop->id), loc_header->length); break; } case DEMO_LOC_H264_EXTRADATA: { - loc_extradata = &ext->value.data; + loc_extradata = &prop->value.data; IMQUIC_LOG(IMQUIC_LOG_INFO, " -- -- %s: %zu bytes\n", - imquic_demo_loc_extension_str(ext->id), + imquic_demo_loc_property_str(prop->id), loc_extradata->length); break; } case DEMO_LOC_OPUS_HEADER: { - loc_header = &ext->value.data; + loc_header = &prop->value.data; IMQUIC_LOG(IMQUIC_LOG_INFO, " -- -- %s: %zu bytes\n", - imquic_demo_loc_extension_str(ext->id), + imquic_demo_loc_property_str(prop->id), loc_header->length); break; } case DEMO_LOC_AAC_HEADER: { - loc_header = &ext->value.data; + loc_header = &prop->value.data; IMQUIC_LOG(IMQUIC_LOG_INFO, " -- -- %s: %zu bytes\n", - imquic_demo_loc_extension_str(ext->id), + imquic_demo_loc_property_str(prop->id), loc_header->length); break; } default: { - IMQUIC_LOG(IMQUIC_LOG_WARN, " -- -- Unknown extension '%"SCNu32"'\n", ext->id); + IMQUIC_LOG(IMQUIC_LOG_WARN, " -- -- Unknown property '%"SCNu32"'\n", prop->id); break; } } @@ -671,7 +583,6 @@ static void imquic_demo_incoming_object(imquic_connection *conn, imquic_moq_obje } else if(object->request_id == 0 && payload_type == DEMO_TYPE_MP4) { /* FIXME Ugly hack: if this is mp4, and our response to request ID 0, subscribe to another track */ uint64_t request_id = 1; - uint64_t track_alias = 1; const char *track_name = "1.m4s"; imquic_moq_namespace tns[32]; /* FIXME */ int i = 0; @@ -684,14 +595,8 @@ static void imquic_demo_incoming_object(imquic_connection *conn, imquic_moq_obje } char tns_buffer[256]; const char *ns = imquic_moq_namespace_str(tns, tns_buffer, sizeof(tns_buffer), TRUE); - if(imquic_moq_get_version(conn) < IMQUIC_MOQ_VERSION_12) { - /* Older versions of MoQ passed the track alias in the SUBSCRIBE */ - IMQUIC_LOG(IMQUIC_LOG_INFO, "[%s] Subscribing to %s/%s (%s), using ID %"SCNu64"/%"SCNu64"\n", - imquic_get_connection_name(conn), ns, track_name, imquic_demo_payload_type_str(payload_type), request_id, track_alias); - } else { - IMQUIC_LOG(IMQUIC_LOG_INFO, "[%s] Subscribing to %s/%s (%s), using ID %"SCNu64"\n", - imquic_get_connection_name(conn), ns, track_name, imquic_demo_payload_type_str(payload_type), request_id); - } + IMQUIC_LOG(IMQUIC_LOG_INFO, "[%s] Subscribing to %s/%s (%s), using ID %"SCNu64"\n", + imquic_get_connection_name(conn), ns, track_name, imquic_demo_payload_type_str(payload_type), request_id); imquic_moq_name tn = { .buffer = (uint8_t *)track_name, .length = strlen(track_name) @@ -718,7 +623,7 @@ static void imquic_demo_incoming_object(imquic_connection *conn, imquic_moq_obje params.subscription_filter.type = filter_type; params.subscription_filter.start_location = start_location; params.subscription_filter.end_group = end_location_sub.group; - imquic_moq_subscribe(conn, request_id, track_alias, &tns[0], &tn, ¶ms); + imquic_moq_subscribe(conn, request_id, 0, &tns[0], &tn, ¶ms); } if(object->end_of_stream) { IMQUIC_LOG(IMQUIC_LOG_INFO, "[%s] Stream closed (status '%s' and eos=%d)\n", @@ -730,9 +635,10 @@ static void imquic_demo_incoming_object(imquic_connection *conn, imquic_moq_obje } } -static void imquic_demo_incoming_go_away(imquic_connection *conn, const char *uri) { +static void imquic_demo_incoming_go_away(imquic_connection *conn, const char *uri, uint64_t timeout) { /* Connection was closed */ - IMQUIC_LOG(IMQUIC_LOG_INFO, "[%s] Got a GOAWAY: %s\n", imquic_get_connection_name(conn), uri); + IMQUIC_LOG(IMQUIC_LOG_INFO, "[%s] Got a GOAWAY: %s (timeout=%"SCNu64"ms)\n", + imquic_get_connection_name(conn), uri, timeout); /* Stop here */ g_atomic_int_inc(&stop); } @@ -802,11 +708,9 @@ int main(int argc, char *argv[]) { IMQUIC_LOG(IMQUIC_LOG_INFO, "Early data support enabled (ticket file '%s')\n", options.ticket_file); if(options.moq_version != NULL) { if(!strcasecmp(options.moq_version, "any")) { - IMQUIC_LOG(IMQUIC_LOG_INFO, "Negotiating version of MoQ between 11 and %d\n", IMQUIC_MOQ_VERSION_MAX - IMQUIC_MOQ_VERSION_BASE); + IMQUIC_LOG(IMQUIC_LOG_INFO, "Negotiating version of MoQ between %d and %d\n", + IMQUIC_MOQ_VERSION_MIN - IMQUIC_MOQ_VERSION_BASE, IMQUIC_MOQ_VERSION_MAX - IMQUIC_MOQ_VERSION_BASE); moq_version = IMQUIC_MOQ_VERSION_ANY; - } else if(!strcasecmp(options.moq_version, "legacy")) { - IMQUIC_LOG(IMQUIC_LOG_INFO, "Negotiating version of MoQ between 6 and 10\n"); - moq_version = IMQUIC_MOQ_VERSION_ANY_LEGACY; } else { moq_version = IMQUIC_MOQ_VERSION_BASE + atoi(options.moq_version); if(moq_version < IMQUIC_MOQ_VERSION_MIN || moq_version > IMQUIC_MOQ_VERSION_MAX) { @@ -833,20 +737,8 @@ int main(int argc, char *argv[]) { goto done; } } else if(options.subscribe_namespace) { - if(moq_version > IMQUIC_MOQ_VERSION_MIN && moq_version < IMQUIC_MOQ_VERSION_12) { - /* Version is too old, we can't: stop here */ - IMQUIC_LOG(IMQUIC_LOG_FATAL, "PUBLISH only supported starting from version 12\n"); - ret = 1; - goto done; - } IMQUIC_LOG(IMQUIC_LOG_INFO, "Using a SUBSCRIBE_NAMESPACE and incoming PUBLISH for the subscription\n"); } else if(options.track_status) { - if(moq_version > IMQUIC_MOQ_VERSION_MIN && moq_version < IMQUIC_MOQ_VERSION_13) { - /* Version is too old, we can't: stop here */ - IMQUIC_LOG(IMQUIC_LOG_FATAL, "TRACK_STATUS only supported starting from version 13\n"); - ret = 1; - goto done; - } IMQUIC_LOG(IMQUIC_LOG_INFO, "Using a TRACK_STATUS for simulating the subscription\n"); } else { IMQUIC_LOG(IMQUIC_LOG_INFO, "Using a SUBSCRIBE for the subscription\n"); @@ -1041,7 +933,6 @@ int main(int argc, char *argv[]) { if(update_time > 0 && g_get_monotonic_time() >= update_time) { /* Send a REQUEST_UPDATE with forward=true */ update_time = 0; - /* TODO This should be done for all subscriptions */ GList *temp = request_ids; while(temp) { uint64_t *rid = (uint64_t *)temp->data; @@ -1057,12 +948,10 @@ int main(int argc, char *argv[]) { params.subscription_filter.type = filter_type; params.subscription_filter.start_location = start_location; params.subscription_filter.end_group = end_location_sub.group; - uint64_t request_id = *rid, sub_request_id = request_id; - if(imquic_moq_get_version(moq_conn) >= IMQUIC_MOQ_VERSION_14) - request_id = imquic_moq_get_next_request_id(moq_conn); + uint64_t request_id = imquic_moq_get_next_request_id(moq_conn); IMQUIC_LOG(IMQUIC_LOG_INFO, "[%s] Sending a REQUEST_UPDATE for ID %"SCNu64" (ID %"SCNu64")\n", imquic_get_connection_name(moq_conn), *rid, request_id); - imquic_moq_update_request(moq_conn, request_id, sub_request_id, ¶ms); + imquic_moq_update_request(moq_conn, request_id, *rid, 0, ¶ms); temp = temp->next; } } diff --git a/examples/moq-test-options.c b/examples/moq-test-options.c index cd846bc..0486f96 100644 --- a/examples/moq-test-options.c +++ b/examples/moq-test-options.c @@ -15,7 +15,7 @@ static GOptionContext *opts = NULL; gboolean demo_options_parse(demo_options *options, int argc, char *argv[]) { /* Supported command-line arguments */ GOptionEntry opt_entries[] = { - { "moq-draft-version", 'M', 0, G_OPTION_ARG_STRING, &options->moq_version, "MoQ draft version number to negotiate (default=any)", "|any|legacy" }, + { "moq-draft-version", 'M', 0, G_OPTION_ARG_STRING, &options->moq_version, "MoQ draft version number to negotiate (default=any)", "|any" }, { "bind", 'b', 0, G_OPTION_ARG_STRING, &options->ip, "Local IP address to bind to (default=all interfaces)", "IP" }, { "port", 'p', 0, G_OPTION_ARG_INT, &options->port, "Local port to bind to (default=0, random)", "port" }, { "raw-quic", 'q', 0, G_OPTION_ARG_NONE, &options->raw_quic, "Whether raw QUIC should be offered for MoQ connections or not (default=no)", NULL }, diff --git a/examples/moq-test.c b/examples/moq-test.c index 6677425..4096e63 100644 --- a/examples/moq-test.c +++ b/examples/moq-test.c @@ -43,6 +43,7 @@ static imquic_moq_version moq_version = IMQUIC_MOQ_VERSION_ANY; static GMutex mutex; static GHashTable *connections = NULL, *subscribers = NULL; static void *imquic_demo_tester_thread(void *data); +static uint64_t moq_track_alias = 0; /* Namespace tuple fields */ typedef enum imquic_demo_tuple_field { @@ -94,9 +95,9 @@ static const char *imquic_demo_tuple_field_str(imquic_demo_tuple_field field) { case TUPLE_FIELD_SEND_EOG: return "Send End of Group Markers"; case TUPLE_FIELD_EXT_INT: - return "Test Integer Extension"; + return "Test Integer Property"; case TUPLE_FIELD_EXT_VAR: - return "Test Variable Extension"; + return "Test Variable Property"; case TUPLE_FIELD_TIMEOUT: return "Publisher Delivery Timeout"; default: @@ -306,20 +307,14 @@ static void imquic_demo_ready(imquic_connection *conn) { peer ? peer : "unknown implementation"); } -static void imquic_demo_incoming_subscribe(imquic_connection *conn, uint64_t request_id, uint64_t track_alias, +static void imquic_demo_incoming_subscribe(imquic_connection *conn, uint64_t request_id, uint64_t required_id_delta, imquic_moq_namespace *tns, imquic_moq_name *tn, imquic_moq_request_parameters *parameters) { /* We received a subscribe */ char tns_buffer[256], tn_buffer[256]; const char *ns = imquic_moq_namespace_str(tns, tns_buffer, sizeof(tns_buffer), TRUE); const char *name = imquic_moq_track_str(tn, tn_buffer, sizeof(tn_buffer)); - if(imquic_moq_get_version(conn) < IMQUIC_MOQ_VERSION_12) { - /* Older versions of MoQ expect the track alias in the SUBSCRIBE */ - IMQUIC_LOG(IMQUIC_LOG_INFO, "[%s] Incoming subscribe for '%s--%s' (ID %"SCNu64"/%"SCNu64")\n", - imquic_get_connection_name(conn), ns, name, request_id, track_alias); - } else { - IMQUIC_LOG(IMQUIC_LOG_INFO, "[%s] Incoming subscribe for '%s--%s' (ID %"SCNu64")\n", - imquic_get_connection_name(conn), ns, name, request_id); - } + IMQUIC_LOG(IMQUIC_LOG_INFO, "[%s] Incoming subscribe for '%s--%s' (ID %"SCNu64")\n", + imquic_get_connection_name(conn), ns, name, request_id); if(parameters->auth_token_set) imquic_moq_print_auth_info(conn, parameters->auth_token, parameters->auth_token_len); /* Parse the namespace tuple to a test profile */ @@ -327,7 +322,7 @@ static void imquic_demo_incoming_subscribe(imquic_connection *conn, uint64_t req char err[256]; int res = imquic_demo_tuple_to_test(conn, tns, test, err, sizeof(err)); if(res != 0) { - imquic_moq_reject_subscribe(conn, request_id, res, err, track_alias, 0); + imquic_moq_reject_subscribe(conn, request_id, res, err, 0); return; } g_mutex_lock(&mutex); @@ -346,11 +341,13 @@ static void imquic_demo_incoming_subscribe(imquic_connection *conn, uint64_t req return; } /* Create a subscription to this track */ - imquic_demo_moq_subscription *s = imquic_demo_moq_subscription_create(sub, request_id, track_alias); + uint64_t new_track_alias = moq_track_alias; + moq_track_alias++; + imquic_demo_moq_subscription *s = imquic_demo_moq_subscription_create(sub, request_id, new_track_alias); memcpy(s->test, test, sizeof(test)); s->forward = parameters->forward; g_hash_table_insert(sub->subscriptions_by_id, imquic_uint64_dup(request_id), s); - g_hash_table_insert(sub->subscriptions, imquic_uint64_dup(track_alias), s); + g_hash_table_insert(sub->subscriptions, imquic_uint64_dup(new_track_alias), s); g_mutex_unlock(&mutex); /* Check the filter */ uint64_t filter_type = parameters->subscription_filter_set ? @@ -387,7 +384,7 @@ static void imquic_demo_incoming_subscribe(imquic_connection *conn, uint64_t req rparams.expires = 0; rparams.group_order_set = TRUE; rparams.group_order = IMQUIC_MOQ_ORDERING_ASCENDING; - imquic_moq_accept_subscribe(conn, request_id, track_alias, &rparams, NULL); + imquic_moq_accept_subscribe(conn, request_id, new_track_alias, &rparams, NULL); /* Spawn thread to send objects */ GError *error = NULL; s->thread = g_thread_try_new("moq-test", &imquic_demo_tester_thread, s, &error); @@ -398,7 +395,8 @@ static void imquic_demo_incoming_subscribe(imquic_connection *conn, uint64_t req } } -static void imquic_demo_request_updated(imquic_connection *conn, uint64_t request_id, uint64_t sub_request_id, imquic_moq_request_parameters *parameters) { +static void imquic_demo_request_updated(imquic_connection *conn, uint64_t request_id, + uint64_t sub_request_id, uint64_t required_id_delta, imquic_moq_request_parameters *parameters) { IMQUIC_LOG(IMQUIC_LOG_INFO, "[%s] Incoming update for subscription%"SCNu64"\n", imquic_get_connection_name(conn), request_id); /* Find the subscriber */ @@ -411,8 +409,7 @@ static void imquic_demo_request_updated(imquic_connection *conn, uint64_t reques return; } /* Update the subscription */ - imquic_demo_moq_subscription *s = g_hash_table_lookup(sub->subscriptions_by_id, - imquic_moq_get_version(conn) >= IMQUIC_MOQ_VERSION_14 ? &sub_request_id : &request_id); + imquic_demo_moq_subscription *s = g_hash_table_lookup(sub->subscriptions_by_id, &sub_request_id); if(s && !s->fetch && parameters->forward_set) { /* TODO Update start location and end group too */ s->forward = parameters->forward; @@ -441,8 +438,8 @@ static void imquic_demo_incoming_unsubscribe(imquic_connection *conn, uint64_t r g_mutex_unlock(&mutex); } -static void imquic_demo_incoming_standalone_fetch(imquic_connection *conn, uint64_t request_id, imquic_moq_namespace *tns, imquic_moq_name *tn, - imquic_moq_location_range *range, imquic_moq_request_parameters *parameters) { +static void imquic_demo_incoming_standalone_fetch(imquic_connection *conn, uint64_t request_id, uint64_t required_id_delta, + imquic_moq_namespace *tns, imquic_moq_name *tn, imquic_moq_location_range *range, imquic_moq_request_parameters *parameters) { /* We received a standalone fetch */ char tns_buffer[256], tn_buffer[256]; const char *ns = imquic_moq_namespace_str(tns, tns_buffer, sizeof(tns_buffer), TRUE); @@ -527,7 +524,7 @@ static void imquic_demo_incoming_standalone_fetch(imquic_connection *conn, uint6 } } -static void imquic_demo_incoming_joining_fetch(imquic_connection *conn, uint64_t request_id, uint64_t joining_request_id , +static void imquic_demo_incoming_joining_fetch(imquic_connection *conn, uint64_t request_id, uint64_t required_id_delta, uint64_t joining_request_id , gboolean absolute, uint64_t joining_start, imquic_moq_request_parameters *parameters) { /* We received a joining fetch */ IMQUIC_LOG(IMQUIC_LOG_INFO, "[%s] Incoming %s joining fetch for subscription %"SCNu64" (ID %"SCNu64"; start=%"SCNu64"; %s order)\n", @@ -607,11 +604,11 @@ static void *imquic_demo_tester_thread(void *data) { uint8_t *obj_p = s->test[TUPLE_FIELD_OBJS_SIZE] ? g_malloc(s->test[TUPLE_FIELD_OBJS_SIZE]) : NULL; if(obj_p) memset(obj_p, 't', s->test[TUPLE_FIELD_OBJS_SIZE]); - size_t extensions_count = 0; + size_t properties_count = 0; if(s->test[TUPLE_FIELD_EXT_INT] >= 0) - extensions_count++; + properties_count++; if(s->test[TUPLE_FIELD_EXT_VAR] >= 0) - extensions_count++; + properties_count++; /* Timers */ int64_t frequency = s->test[TUPLE_FIELD_OBJS_FREQ] * 1000; int64_t sleep_time = frequency/2; @@ -662,21 +659,21 @@ static void *imquic_demo_tester_thread(void *data) { (s->descending && (object_id == 0 || (group_id == (uint64_t)s->test[TUPLE_FIELD_START_GROUP] && object_id <= (uint64_t)s->test[TUPLE_FIELD_START_OBJECT])))) last_object = TRUE; } - GList *exts = NULL; - if(extensions_count > 0) { - imquic_moq_object_extension numext = { 0 }, dataext = { 0 }; + GList *props = NULL; + if(properties_count > 0) { + imquic_moq_property numprop = { 0 }, dataprop = { 0 }; if(s->test[TUPLE_FIELD_EXT_INT] >= 0) { - /* Add a numeric extension */ - numext.id = 2 * s->test[TUPLE_FIELD_EXT_INT]; - numext.value.number = g_random_int(); - exts = g_list_append(exts, &numext); + /* Add a numeric property */ + numprop.id = 2 * s->test[TUPLE_FIELD_EXT_INT]; + numprop.value.number = g_random_int(); + props = g_list_append(props, &numprop); } if(s->test[TUPLE_FIELD_EXT_VAR] >= 0) { - /* Add a data extension */ - dataext.id = 2 * s->test[TUPLE_FIELD_EXT_VAR] + 1; - dataext.value.data.buffer = (uint8_t *)"moq-test"; - dataext.value.data.length = strlen("moq-test"); - exts = g_list_append(exts, &dataext); + /* Add a data property */ + dataprop.id = 2 * s->test[TUPLE_FIELD_EXT_VAR] + 1; + dataprop.value.data.buffer = (uint8_t *)"moq-test"; + dataprop.value.data.length = strlen("moq-test"); + props = g_list_append(props, &dataprop); } } imquic_moq_object object = { @@ -688,7 +685,7 @@ static void *imquic_demo_tester_thread(void *data) { .object_status = 0, .payload = (num_objects == 0) ? obj0_p : obj_p, .payload_len = (num_objects == 0) ? s->test[TUPLE_FIELD_OBJ0_SIZE] : s->test[TUPLE_FIELD_OBJS_SIZE], - .extensions = exts, + .properties = props, .delivery = delivery, .end_of_stream = (last_object || (!s->fetch && num_objects == (s->test[TUPLE_FIELD_OBJS_x_GROUP] - 1) && !s->test[TUPLE_FIELD_SEND_EOG])) }; @@ -698,7 +695,7 @@ static void *imquic_demo_tester_thread(void *data) { last_subgroup_id = object.subgroup_id; last_object_id = object.object_id; } - g_list_free(exts); + g_list_free(props); /* Update IDs for the next object */ num_objects++; next_group = (num_objects == s->test[TUPLE_FIELD_OBJS_x_GROUP]); @@ -715,7 +712,7 @@ static void *imquic_demo_tester_thread(void *data) { object.object_status = last_object ? IMQUIC_MOQ_END_OF_TRACK : IMQUIC_MOQ_END_OF_GROUP; object.payload_len = 0; object.payload = NULL; - object.extensions = NULL; + object.properties = NULL; object.end_of_stream = TRUE; if(send_object || last_object) imquic_moq_send_object(conn, &object); @@ -827,11 +824,9 @@ int main(int argc, char *argv[]) { if(options.moq_version != NULL) { if(!strcasecmp(options.moq_version, "any")) { - IMQUIC_LOG(IMQUIC_LOG_INFO, "Negotiating version of MoQ between 11 and %d\n", IMQUIC_MOQ_VERSION_MAX - IMQUIC_MOQ_VERSION_BASE); + IMQUIC_LOG(IMQUIC_LOG_INFO, "Negotiating version of MoQ between %d and %d\n", + IMQUIC_MOQ_VERSION_MIN - IMQUIC_MOQ_VERSION_BASE, IMQUIC_MOQ_VERSION_MAX - IMQUIC_MOQ_VERSION_BASE); moq_version = IMQUIC_MOQ_VERSION_ANY; - } else if(!strcasecmp(options.moq_version, "legacy")) { - IMQUIC_LOG(IMQUIC_LOG_INFO, "Negotiating version of MoQ between 6 and 10\n"); - moq_version = IMQUIC_MOQ_VERSION_ANY_LEGACY; } else { moq_version = IMQUIC_MOQ_VERSION_BASE + atoi(options.moq_version); if(moq_version < IMQUIC_MOQ_VERSION_MIN || moq_version > IMQUIC_MOQ_VERSION_MAX) { @@ -940,8 +935,8 @@ int main(int argc, char *argv[]) { default_test[TUPLE_FIELD_GROUP_INC] = 1; default_test[TUPLE_FIELD_OBJ_INC] = 1; default_test[TUPLE_FIELD_SEND_EOG] = 0; - default_test[TUPLE_FIELD_EXT_INT] = -1; /* Don't add any numeric extension by default */ - default_test[TUPLE_FIELD_EXT_VAR] = -1; /* Don't add any variable extension by default */ + default_test[TUPLE_FIELD_EXT_INT] = -1; /* Don't add any numeric property by default */ + default_test[TUPLE_FIELD_EXT_VAR] = -1; /* Don't add any variable property by default */ default_test[TUPLE_FIELD_TIMEOUT] = -1; /* No delivery timeout by default */ /* Initialize the resources we'll need */ diff --git a/examples/moq-utils.c b/examples/moq-utils.c index 2f8acfc..c015275 100644 --- a/examples/moq-utils.c +++ b/examples/moq-utils.c @@ -22,51 +22,68 @@ imquic_moq_object *imquic_moq_object_duplicate(imquic_moq_object *object) { new_obj->payload = g_malloc(object->payload_len); memcpy(new_obj->payload, object->payload, object->payload_len); } - new_obj->extensions = imquic_moq_object_extensions_duplicate(object->extensions); + new_obj->properties = imquic_moq_properties_duplicate(object->properties); return new_obj; } -/* Helper to duplicate a list of extensions */ -GList *imquic_moq_object_extensions_duplicate(GList *extensions) { - if(extensions == NULL) +/* Helper to print a list of properties */ +void imquic_moq_properties_print(GList *properties) { + GList *temp = properties; + while(temp) { + imquic_moq_property *prop = (imquic_moq_property *)temp->data; + const char *prop_name = imquic_moq_property_type_str(prop->id); + if(prop->id % 2 == 0) { + IMQUIC_LOG(IMQUIC_LOG_INFO, " >> Property '%"SCNu32"' (%s) = %"SCNu64"\n", + prop->id, (prop_name ? prop_name : "unknown"), prop->value.number); + } else { + IMQUIC_LOG(IMQUIC_LOG_INFO, " >> Property '%"SCNu32"' (%s) = %.*s\n", + prop->id, (prop_name ? prop_name : "unknown"), (int)prop->value.data.length, prop->value.data.buffer); + } + temp = temp->next; + } +} + +/* Helper to duplicate a list of properties */ +GList *imquic_moq_properties_duplicate(GList *properties) { + if(properties == NULL) return NULL; - GList *new_extensions = NULL; - GList *temp = extensions; + GList *new_properties = NULL; + GList *temp = properties; while(temp) { - imquic_moq_object_extension *ext = (imquic_moq_object_extension *)temp->data; - imquic_moq_object_extension *new_ext = g_malloc0(sizeof(imquic_moq_object_extension)); - new_ext->id = ext->id; - if(ext->id % 2 == 0) { - new_ext->value.number = ext->value.number; + imquic_moq_property *prop = (imquic_moq_property *)temp->data; + imquic_moq_property *new_prop = g_malloc0(sizeof(imquic_moq_property)); + new_prop->id = prop->id; + if(prop->id % 2 == 0) { + new_prop->value.number = prop->value.number; } else { - new_ext->value.data.length = ext->value.data.length; - if(ext->value.data.length > 0) { - new_ext->value.data.buffer = g_malloc(ext->value.data.length); - memcpy(new_ext->value.data.buffer, ext->value.data.buffer, ext->value.data.length); + new_prop->value.data.length = prop->value.data.length; + if(prop->value.data.length > 0) { + new_prop->value.data.buffer = g_malloc(prop->value.data.length); + memcpy(new_prop->value.data.buffer, prop->value.data.buffer, prop->value.data.length); } } - new_extensions = g_list_prepend(new_extensions, new_ext); + new_properties = g_list_prepend(new_properties, new_prop); temp = temp->next; } - new_extensions = g_list_reverse(new_extensions); - return new_extensions; + new_properties = g_list_reverse(new_properties); + return new_properties; } /* Helper to destroy a duplicated object */ void imquic_moq_object_cleanup(imquic_moq_object *object) { if(object) { g_free(object->payload); - g_list_free_full(object->extensions, (GDestroyNotify)(imquic_moq_object_extension_cleanup)); + g_list_free_full(object->properties, (GDestroyNotify)(imquic_moq_property_cleanup)); g_free(object); } } -/* Helper to destroy an object extension */ -void imquic_moq_object_extension_cleanup(imquic_moq_object_extension *extension) { - if(extension != NULL) { - if(extension->value.data.buffer != NULL) - g_free(extension->value.data.buffer); - g_free(extension); +/* Helper to destroy an object propertie */ +void imquic_moq_property_cleanup(imquic_moq_property *property) { + if(property != NULL) { + if(property->value.data.buffer != NULL) + g_free(property->value.data.buffer); + g_free(property); } } @@ -74,41 +91,29 @@ void imquic_moq_object_extension_cleanup(imquic_moq_object_extension *extension) int imquic_moq_auth_info_to_bytes(imquic_connection *conn, const char *auth_info, uint8_t *auth, size_t *authlen) { if(conn == NULL || auth_info == NULL || auth == NULL || authlen == 0) return -1; - if(imquic_moq_get_version(conn) < IMQUIC_MOQ_VERSION_11) { - /* Just copy the string to the buffer */ - *authlen = strlen(auth_info); - memcpy(auth, auth_info, *authlen); - } else { - /* Serialize the token using the USE_VALUE alias type */ - imquic_moq_auth_token token = { 0 }; - token.alias_type = IMQUIC_MOQ_AUTH_TOKEN_USE_VALUE; - token.token_type_set = TRUE; - token.token_type = 0; /* FIXME */ - token.token_value.buffer = (uint8_t *)auth_info; - token.token_value.length = strlen(auth_info); - size_t offset = imquic_moq_build_auth_token(&token, auth, *authlen); - if(offset == 0) - return -1; - *authlen = offset; - } + /* Serialize the token using the USE_VALUE alias type */ + imquic_moq_auth_token token = { 0 }; + token.alias_type = IMQUIC_MOQ_AUTH_TOKEN_USE_VALUE; + token.token_type_set = TRUE; + token.token_type = 0; /* FIXME */ + token.token_value.buffer = (uint8_t *)auth_info; + token.token_value.length = strlen(auth_info); + size_t offset = imquic_moq_build_auth_token(imquic_moq_get_version(conn), &token, auth, *authlen); + if(offset == 0) + return -1; + *authlen = offset; return 0; } void imquic_moq_print_auth_info(imquic_connection *conn, uint8_t *auth, size_t authlen) { if(conn == NULL || auth == NULL || authlen == 0) return; - if(imquic_moq_get_version(conn) < IMQUIC_MOQ_VERSION_11) { - /* String */ - IMQUIC_LOG(IMQUIC_LOG_INFO, "[%s] -- Authorization info: %.*s\n", - imquic_get_connection_name(conn), (int)authlen, auth); - } else { - /* Data (hex) */ - IMQUIC_LOG(IMQUIC_LOG_INFO, "[%s] -- Authorization info: ", - imquic_get_connection_name(conn)); - for(size_t i=0; istreams, &stream_id); if(stream == NULL || !stream->can_send) { imquic_mutex_unlock(&conn->mutex); - IMQUIC_LOG(IMQUIC_LOG_WARN, "[%s] Couldn't close stream, no such stream %"SCNu64"\n", + IMQUIC_LOG(IMQUIC_LOG_WARN, "[%s] Couldn't reset stream, no such stream %"SCNu64"\n", imquic_get_connection_name(conn), stream_id); return; } @@ -256,7 +256,7 @@ void imquic_connection_reset_stream(imquic_connection *conn, uint64_t stream_id, imquic_stream_mark_complete(stream, FALSE); stream->out_state = IMQUIC_STREAM_RESET; imquic_mutex_unlock(&stream->mutex); - IMQUIC_LOG(IMQUIC_LOG_INFO, "[%s] Closing stream %"SCNu64" (RESET_STREAM)\n", + IMQUIC_LOG(IMQUIC_LOG_INFO, "[%s] Resetting stream %"SCNu64" (RESET_STREAM)\n", imquic_get_connection_name(conn), stream_id); imquic_connection_event *event = imquic_connection_event_create(IMQUIC_CONNECTION_EVENT_RESET_STREAM); event->stream_id = stream_id; @@ -267,6 +267,40 @@ void imquic_connection_reset_stream(imquic_connection *conn, uint64_t stream_id, imquic_refcount_decrease(&stream->ref); } +/* Helper to stop a STREAM */ +void imquic_connection_stop_sending_stream(imquic_connection *conn, uint64_t stream_id, uint64_t error_code) { + if(conn == NULL) + return; + imquic_mutex_lock(&conn->mutex); + imquic_stream *stream = g_hash_table_lookup(conn->streams, &stream_id); + if(stream == NULL || !stream->can_send) { + imquic_mutex_unlock(&conn->mutex); + IMQUIC_LOG(IMQUIC_LOG_WARN, "[%s] Couldn't stop stream, no such stream %"SCNu64"\n", + imquic_get_connection_name(conn), stream_id); + return; + } + imquic_refcount_increase(&stream->ref); + imquic_mutex_unlock(&conn->mutex); + imquic_mutex_lock(&stream->mutex); + if(stream->in_state >= IMQUIC_STREAM_RESET) { + imquic_mutex_unlock(&stream->mutex); + IMQUIC_LOG(IMQUIC_LOG_WARN, "[%s] Couldn't prepare STOP_SENDING for %"SCNu64" (alreayd sent?)\n", + imquic_get_connection_name(conn), stream_id); + } else { + imquic_stream_mark_complete(stream, FALSE); + stream->in_state = IMQUIC_STREAM_RESET; + imquic_mutex_unlock(&stream->mutex); + IMQUIC_LOG(IMQUIC_LOG_INFO, "[%s] Stopping stream %"SCNu64" (RESET_STREAM)\n", + imquic_get_connection_name(conn), stream_id); + imquic_connection_event *event = imquic_connection_event_create(IMQUIC_CONNECTION_EVENT_STOP_SENDING); + event->stream_id = stream_id; + event->error_code = error_code; + g_async_queue_push(conn->queued_events, event); + imquic_loop_wakeup(); + } + imquic_refcount_decrease(&stream->ref); +} + /* Helpers to close connections */ void imquic_connection_close(imquic_connection *conn, uint64_t error_code, const char *reason) { /* Send a CONNECTION CLOSE */ diff --git a/src/imquic-moq.c b/src/imquic-moq.c index 3b8029d..ca77508 100644 --- a/src/imquic-moq.c +++ b/src/imquic-moq.c @@ -117,6 +117,7 @@ imquic_server *imquic_create_moq_server(const char *name, ...) { server->stream_incoming = imquic_moq_stream_incoming; server->datagram_incoming = imquic_moq_datagram_incoming; server->reset_stream_incoming = imquic_moq_reset_stream_incoming; + server->stop_sending_incoming = imquic_moq_stop_sending_incoming; server->connection_gone = imquic_moq_connection_gone; return server; } @@ -219,6 +220,7 @@ imquic_client *imquic_create_moq_client(const char *name, ...) { client->stream_incoming = imquic_moq_stream_incoming; client->datagram_incoming = imquic_moq_datagram_incoming; client->reset_stream_incoming = imquic_moq_reset_stream_incoming; + client->stop_sending_incoming = imquic_moq_stop_sending_incoming; client->connection_gone = imquic_moq_connection_gone; return client; } @@ -251,9 +253,11 @@ static size_t imquic_moq_name_render(uint8_t *data, size_t dlen, char *buffer, s } const char *imquic_moq_namespace_str(imquic_moq_namespace *tns, char *buffer, size_t blen, gboolean tuple) { - if(tns == NULL) + if(buffer == NULL) return NULL; *buffer = '\0'; + if(tns == NULL) + return buffer; size_t offset = 0; while(tns != NULL) { if(blen - offset == 0) @@ -297,7 +301,9 @@ gboolean imquic_moq_namespace_equals(imquic_moq_namespace *first, imquic_moq_nam } gboolean imquic_moq_namespace_contains(imquic_moq_namespace *parent, imquic_moq_namespace *child) { - if(parent == NULL || child == NULL) + if(parent == NULL) + return TRUE; + if(child == NULL) return FALSE; size_t i = 0; while(parent) { @@ -418,7 +424,7 @@ void imquic_set_moq_ready_cb(imquic_endpoint *endpoint, } void imquic_set_incoming_publish_namespace_cb(imquic_endpoint *endpoint, - void (* incoming_publish_namespace)(imquic_connection *conn, uint64_t request_id, imquic_moq_namespace *tns, imquic_moq_request_parameters *parameters)) { + void (* incoming_publish_namespace)(imquic_connection *conn, uint64_t request_id, uint64_t required_id_delta, imquic_moq_namespace *tns, imquic_moq_request_parameters *parameters)) { if(endpoint != NULL) { if(endpoint->protocol != IMQUIC_MOQ) { IMQUIC_LOG(IMQUIC_LOG_WARN, "Can't set MoQ callback on non-MoQ endpoint\n"); @@ -429,7 +435,7 @@ void imquic_set_incoming_publish_namespace_cb(imquic_endpoint *endpoint, } void imquic_set_incoming_publish_namespace_cancel_cb(imquic_endpoint *endpoint, - void (* incoming_publish_namespace_cancel)(imquic_connection *conn, imquic_moq_namespace *tns, imquic_moq_request_error_code error_code, const char *reason)) { + void (* incoming_publish_namespace_cancel)(imquic_connection *conn, uint64_t request_id, imquic_moq_request_error_code error_code, const char *reason)) { if(endpoint != NULL) { if(endpoint->protocol != IMQUIC_MOQ) { IMQUIC_LOG(IMQUIC_LOG_WARN, "Can't set MoQ callback on non-MoQ endpoint\n"); @@ -462,7 +468,7 @@ void imquic_set_publish_namespace_error_cb(imquic_endpoint *endpoint, } void imquic_set_publish_namespace_done_cb(imquic_endpoint *endpoint, - void (* publish_namespace_done)(imquic_connection *conn, imquic_moq_namespace *tns)) { + void (* publish_namespace_done)(imquic_connection *conn, uint64_t request_id)) { if(endpoint != NULL) { if(endpoint->protocol != IMQUIC_MOQ) { IMQUIC_LOG(IMQUIC_LOG_WARN, "Can't set MoQ callback on non-MoQ endpoint\n"); @@ -473,8 +479,8 @@ void imquic_set_publish_namespace_done_cb(imquic_endpoint *endpoint, } void imquic_set_incoming_publish_cb(imquic_endpoint *endpoint, - void (* incoming_publish)(imquic_connection *conn, uint64_t request_id, imquic_moq_namespace *tns, imquic_moq_name *tn, - uint64_t track_alias, imquic_moq_request_parameters *parameters, GList *track_extensions)) { + void (* incoming_publish)(imquic_connection *conn, uint64_t request_id, uint64_t required_id_delta, imquic_moq_namespace *tns, imquic_moq_name *tn, + uint64_t track_alias, imquic_moq_request_parameters *parameters, GList *track_properties)) { if(endpoint != NULL) { if(endpoint->protocol != IMQUIC_MOQ) { IMQUIC_LOG(IMQUIC_LOG_WARN, "Can't set MoQ callback on non-MoQ endpoint\n"); @@ -507,7 +513,7 @@ void imquic_set_publish_error_cb(imquic_endpoint *endpoint, } void imquic_set_incoming_subscribe_cb(imquic_endpoint *endpoint, - void (* incoming_subscribe)(imquic_connection *conn, uint64_t request_id, uint64_t track_alias, + void (* incoming_subscribe)(imquic_connection *conn, uint64_t request_id, uint64_t required_id_delta, imquic_moq_namespace *tns, imquic_moq_name *tn, imquic_moq_request_parameters *parameters)) { if(endpoint != NULL) { if(endpoint->protocol != IMQUIC_MOQ) { @@ -519,7 +525,7 @@ void imquic_set_incoming_subscribe_cb(imquic_endpoint *endpoint, } void imquic_set_subscribe_accepted_cb(imquic_endpoint *endpoint, - void (* subscribe_accepted)(imquic_connection *conn, uint64_t request_id, uint64_t track_alias, imquic_moq_request_parameters *parameters, GList *track_extensions)) { + void (* subscribe_accepted)(imquic_connection *conn, uint64_t request_id, uint64_t track_alias, imquic_moq_request_parameters *parameters, GList *track_properties)) { if(endpoint != NULL) { if(endpoint->protocol != IMQUIC_MOQ) { IMQUIC_LOG(IMQUIC_LOG_WARN, "Can't set MoQ callback on non-MoQ endpoint\n"); @@ -530,7 +536,7 @@ void imquic_set_subscribe_accepted_cb(imquic_endpoint *endpoint, } void imquic_set_subscribe_error_cb(imquic_endpoint *endpoint, - void (* subscribe_error)(imquic_connection *conn, uint64_t request_id, imquic_moq_request_error_code error_codes, const char *reason, uint64_t track_alias, uint64_t retry_interval)) { + void (* subscribe_error)(imquic_connection *conn, uint64_t request_id, imquic_moq_request_error_code error_codes, const char *reason, uint64_t retry_interval)) { if(endpoint != NULL) { if(endpoint->protocol != IMQUIC_MOQ) { IMQUIC_LOG(IMQUIC_LOG_WARN, "Can't set MoQ callback on non-MoQ endpoint\n"); @@ -541,7 +547,7 @@ void imquic_set_subscribe_error_cb(imquic_endpoint *endpoint, } void imquic_set_request_updated_cb(imquic_endpoint *endpoint, - void (* request_updated)(imquic_connection *conn, uint64_t request_id, uint64_t sub_request_id, imquic_moq_request_parameters *parameters)) { + void (* request_updated)(imquic_connection *conn, uint64_t request_id, uint64_t sub_request_id, uint64_t required_id_delta, imquic_moq_request_parameters *parameters)) { if(endpoint != NULL) { if(endpoint->protocol != IMQUIC_MOQ) { IMQUIC_LOG(IMQUIC_LOG_WARN, "Can't set MoQ callback on non-MoQ endpoint\n"); @@ -607,7 +613,7 @@ void imquic_set_requests_blocked_cb(imquic_endpoint *endpoint, } void imquic_set_incoming_subscribe_namespace_cb(imquic_endpoint *endpoint, - void (* incoming_subscribe_namespace)(imquic_connection *conn, uint64_t request_id, + void (* incoming_subscribe_namespace)(imquic_connection *conn, uint64_t request_id, uint64_t required_id_delta, imquic_moq_namespace *tns, imquic_moq_subscribe_namespace_options subscribe_options, imquic_moq_request_parameters *parameters)) { if(endpoint != NULL) { if(endpoint->protocol != IMQUIC_MOQ) { @@ -641,7 +647,7 @@ void imquic_set_subscribe_namespace_error_cb(imquic_endpoint *endpoint, } void imquic_set_incoming_unsubscribe_namespace_cb(imquic_endpoint *endpoint, - void (* incoming_unsubscribe_namespace)(imquic_connection *conn, uint64_t request_id, imquic_moq_namespace *tns)) { + void (* incoming_unsubscribe_namespace)(imquic_connection *conn, uint64_t request_id)) { if(endpoint != NULL) { if(endpoint->protocol != IMQUIC_MOQ) { IMQUIC_LOG(IMQUIC_LOG_WARN, "Can't set MoQ callback on non-MoQ endpoint\n"); @@ -652,7 +658,7 @@ void imquic_set_incoming_unsubscribe_namespace_cb(imquic_endpoint *endpoint, } void imquic_set_incoming_namespace_cb(imquic_endpoint *endpoint, - void (* incoming_namespace)(imquic_connection *conn, uint64_t request_id, imquic_moq_namespace *tns_suffix)) { + void (* incoming_namespace)(imquic_connection *conn, uint64_t request_id, imquic_moq_namespace *tns)) { if(endpoint != NULL) { if(endpoint->protocol != IMQUIC_MOQ) { IMQUIC_LOG(IMQUIC_LOG_WARN, "Can't set MoQ callback on non-MoQ endpoint\n"); @@ -663,7 +669,7 @@ void imquic_set_incoming_namespace_cb(imquic_endpoint *endpoint, } void imquic_set_incoming_namespace_done_cb(imquic_endpoint *endpoint, - void (* incoming_namespace_done)(imquic_connection *conn, uint64_t request_id, imquic_moq_namespace *tns_suffix)) { + void (* incoming_namespace_done)(imquic_connection *conn, uint64_t request_id, imquic_moq_namespace *tns)) { if(endpoint != NULL) { if(endpoint->protocol != IMQUIC_MOQ) { IMQUIC_LOG(IMQUIC_LOG_WARN, "Can't set MoQ callback on non-MoQ endpoint\n"); @@ -673,8 +679,19 @@ void imquic_set_incoming_namespace_done_cb(imquic_endpoint *endpoint, } } +void imquic_set_incoming_publish_blocked_cb(imquic_endpoint *endpoint, + void (* incoming_publish_blocked)(imquic_connection *conn, uint64_t request_id, imquic_moq_namespace *tns, imquic_moq_name *tn)) { + if(endpoint != NULL) { + if(endpoint->protocol != IMQUIC_MOQ) { + IMQUIC_LOG(IMQUIC_LOG_WARN, "Can't set MoQ callback on non-MoQ endpoint\n"); + return; + } + endpoint->callbacks.moq.incoming_publish_blocked = incoming_publish_blocked; + } +} + void imquic_set_incoming_standalone_fetch_cb(imquic_endpoint *endpoint, - void (* incoming_standalone_fetch)(imquic_connection *conn, uint64_t request_id, + void (* incoming_standalone_fetch)(imquic_connection *conn, uint64_t request_id, uint64_t required_id_delta, imquic_moq_namespace *tns, imquic_moq_name *tn, imquic_moq_location_range *range, imquic_moq_request_parameters *parameters)) { if(endpoint != NULL) { if(endpoint->protocol != IMQUIC_MOQ) { @@ -686,7 +703,7 @@ void imquic_set_incoming_standalone_fetch_cb(imquic_endpoint *endpoint, } void imquic_set_incoming_joining_fetch_cb(imquic_endpoint *endpoint, - void (* incoming_joining_fetch)(imquic_connection *conn, uint64_t request_id, uint64_t joining_request_id, + void (* incoming_joining_fetch)(imquic_connection *conn, uint64_t request_id, uint64_t required_id_delta, uint64_t joining_request_id, gboolean absolute, uint64_t joining_start, imquic_moq_request_parameters *parameters)) { if(endpoint != NULL) { if(endpoint->protocol != IMQUIC_MOQ) { @@ -709,7 +726,7 @@ void imquic_set_incoming_fetch_cancel_cb(imquic_endpoint *endpoint, } void imquic_set_fetch_accepted_cb(imquic_endpoint *endpoint, - void (* fetch_accepted)(imquic_connection *conn, uint64_t request_id, imquic_moq_location *largest, imquic_moq_request_parameters *parameters, GList *track_extensions)) { + void (* fetch_accepted)(imquic_connection *conn, uint64_t request_id, imquic_moq_location *largest, imquic_moq_request_parameters *parameters, GList *track_properties)) { if(endpoint != NULL) { if(endpoint->protocol != IMQUIC_MOQ) { IMQUIC_LOG(IMQUIC_LOG_WARN, "Can't set MoQ callback on non-MoQ endpoint\n"); @@ -743,7 +760,7 @@ void imquic_set_incoming_track_status_cb(imquic_endpoint *endpoint, } void imquic_set_track_status_accepted_cb(imquic_endpoint *endpoint, - void (* track_status_accepted)(imquic_connection *conn, uint64_t request_id, uint64_t track_alias, imquic_moq_request_parameters *parameters)) { + void (* track_status_accepted)(imquic_connection *conn, uint64_t request_id, imquic_moq_request_parameters *parameters)) { if(endpoint != NULL) { if(endpoint->protocol != IMQUIC_MOQ) { IMQUIC_LOG(IMQUIC_LOG_WARN, "Can't set MoQ callback on non-MoQ endpoint\n"); @@ -776,7 +793,7 @@ void imquic_set_incoming_object_cb(imquic_endpoint *endpoint, } void imquic_set_incoming_goaway_cb(imquic_endpoint *endpoint, - void (* incoming_goaway)(imquic_connection *conn, const char *uri)) { + void (* incoming_goaway)(imquic_connection *conn, const char *uri, uint64_t timeout)) { if(endpoint != NULL) { if(endpoint->protocol != IMQUIC_MOQ) { IMQUIC_LOG(IMQUIC_LOG_WARN, "Can't set MoQ callback on non-MoQ endpoint\n"); @@ -800,22 +817,12 @@ void imquic_set_moq_connection_gone_cb(imquic_endpoint *endpoint, /* Versions */ const char *imquic_moq_version_str(imquic_moq_version version) { switch(version) { - case IMQUIC_MOQ_VERSION_11: - return "draft-ietf-moq-transport-11"; - case IMQUIC_MOQ_VERSION_12: - return "draft-ietf-moq-transport-12"; - case IMQUIC_MOQ_VERSION_13: - return "draft-ietf-moq-transport-13"; - case IMQUIC_MOQ_VERSION_14: - return "draft-ietf-moq-transport-14"; - case IMQUIC_MOQ_VERSION_15: - return "draft-ietf-moq-transport-15"; case IMQUIC_MOQ_VERSION_16: return "draft-ietf-moq-transport-16"; + case IMQUIC_MOQ_VERSION_17: + return "draft-ietf-moq-transport-17"; case IMQUIC_MOQ_VERSION_ANY: - return "draft-ietf-moq-transport-XX(-from--11-to-16)"; - case IMQUIC_MOQ_VERSION_ANY_LEGACY: - return "draft-ietf-moq-transport-XX(-from-11-to-14)"; + return "draft-ietf-moq-transport-XX(-from--16-to-17)"; default: break; } return NULL; @@ -823,22 +830,12 @@ const char *imquic_moq_version_str(imquic_moq_version version) { static const char *imquic_moq_version_alpn(imquic_moq_version version) { switch(version) { - case IMQUIC_MOQ_VERSION_11: - return "moq-11,moq-00"; - case IMQUIC_MOQ_VERSION_12: - return "moq-12,moq-00"; - case IMQUIC_MOQ_VERSION_13: - return "moq-13,moq-00"; - case IMQUIC_MOQ_VERSION_14: - return "moq-14,moq-00"; - case IMQUIC_MOQ_VERSION_15: - return "moq-15"; case IMQUIC_MOQ_VERSION_16: return "moq-16"; + case IMQUIC_MOQ_VERSION_17: + return "moq-17"; case IMQUIC_MOQ_VERSION_ANY: - return "moq-16,moq-15,moq-14,moq-13,moq-12,moq-11,moq-00"; - case IMQUIC_MOQ_VERSION_ANY_LEGACY: - return "moq-14,moq-13,moq-12,moq-11,moq-00"; + return "moq-17,moq-16"; default: break; } return NULL; @@ -863,8 +860,6 @@ const char *imquic_moq_object_status_str(imquic_moq_object_status status) { switch(status) { case IMQUIC_MOQ_NORMAL_OBJECT: return "NORMAL_OBJECT"; - case IMQUIC_MOQ_OBJECT_DOESNT_EXIST: - return "OBJECT_DOESNT_EXIST"; case IMQUIC_MOQ_END_OF_GROUP: return "END_OF_GROUP"; case IMQUIC_MOQ_END_OF_TRACK: @@ -874,24 +869,24 @@ const char *imquic_moq_object_status_str(imquic_moq_object_status status) { return NULL; } -/* Extension header types */ -const char *imquic_moq_extension_type_str(imquic_moq_extension_type type) { +/* Property types */ +const char *imquic_moq_property_type_str(imquic_moq_property_type type) { switch(type) { - case IMQUIC_MOQ_EXT_DELIVERY_TIMEOUT: + case IMQUIC_MOQ_PROPERTY_DELIVERY_TIMEOUT: return "Delivery Timeout"; - case IMQUIC_MOQ_EXT_MAX_CACHE_DURATION: + case IMQUIC_MOQ_PROPERTY_MAX_CACHE_DURATION: return "Max Cache Duration"; - case IMQUIC_MOQ_EXT_DEFAULT_PUBLISHER_PRIORITY: + case IMQUIC_MOQ_PROPERTY_DEFAULT_PUBLISHER_PRIORITY: return "Default Publisher Priority"; - case IMQUIC_MOQ_EXT_DEFAULT_GROUP_ORDER: + case IMQUIC_MOQ_PROPERTY_DEFAULT_GROUP_ORDER: return "Default Group Order"; - case IMQUIC_MOQ_EXT_DYNAMIC_GROUPS: + case IMQUIC_MOQ_PROPERTY_DYNAMIC_GROUPS: return "Dynamic Groups"; - case IMQUIC_MOQ_EXT_PRIOR_GROUP_ID_GAP: + case IMQUIC_MOQ_PROPERTY_PRIOR_GROUP_ID_GAP: return "Prior Group ID Gap"; - case IMQUIC_MOQ_EXT_PRIOR_OBJECT_ID_GAP: + case IMQUIC_MOQ_PROPERTY_PRIOR_OBJECT_ID_GAP: return "Prior Object ID Gap"; - case IMQUIC_MOQ_EXT_IMMUTABLE_EXTENSIONS: + case IMQUIC_MOQ_PROPERTY_IMMUTABLE_PROPERTIES: return "Immutable Extensions"; default: break; } diff --git a/src/imquic.c b/src/imquic.c index ff30918..d0cf86b 100644 --- a/src/imquic.c +++ b/src/imquic.c @@ -469,6 +469,17 @@ void imquic_set_reset_stream_cb(imquic_endpoint *endpoint, } } +void imquic_set_stop_sending_cb(imquic_endpoint *endpoint, + void (* stop_sending_incoming)(imquic_connection *conn, uint64_t stream_id, uint64_t error_code)) { + if(endpoint != NULL) { + if(endpoint->internal_callbacks) { + IMQUIC_LOG(IMQUIC_LOG_WARN, "Can't seq QUIC callback when using specific protocol handler\n"); + } else { + endpoint->stop_sending_incoming = stop_sending_incoming; + } + } +} + void imquic_set_connection_failed_cb(imquic_endpoint *endpoint, void (* connection_failed)(void *user_data)) { if(endpoint != NULL && !endpoint->is_server) diff --git a/src/imquic/imquic.h b/src/imquic/imquic.h index 30f5098..f42eb7d 100644 --- a/src/imquic/imquic.h +++ b/src/imquic/imquic.h @@ -463,6 +463,12 @@ void imquic_set_datagram_incoming_cb(imquic_endpoint *endpoint, * @param reset_stream_incoming Pointer to the function that will be invoked on the new RESET_STREAM */ void imquic_set_reset_stream_cb(imquic_endpoint *endpoint, void (* reset_stream_incoming)(imquic_connection *conn, uint64_t stream_id, uint64_t error_code)); +/*! \brief Configure the callback function to be notified about incoming + * STOP_SENDING messages on an existing connection handled by this endpoint. + * @param endpoint The imquic_endpoint (imquic_server or imquic_client) to configure + * @param reset_stop_sending Pointer to the function that will be invoked on the new RESET_STREAM */ +void imquic_set_stop_sending_cb(imquic_endpoint *endpoint, + void (* reset_stop_sending)(imquic_connection *conn, uint64_t stream_id, uint64_t error_code)); /*! \brief Configure the callback function to be notified when an attemp * to establish a connection failed, e.g., because the server is unreachable. * @note Considering the application never received a connection instance diff --git a/src/imquic/moq.h b/src/imquic/moq.h index 585fa14..4ca6e5f 100644 --- a/src/imquic/moq.h +++ b/src/imquic/moq.h @@ -34,13 +34,9 @@ * \ref IMQUIC_MOQ_VERSION_ANY , which means that for clients it will offer * all supported versions, while for servers it will accept the first * offered among the supported ones, when negotiared via ALPN or - * WebTransort protocol; a "legacy" version called \ref IMQUIC_MOQ_VERSION_ANY_LEGACY - * is available, to negotiate any supported version between v11 and v14. - * The reason for this separation of version negotiation in different - * groups is due to the incompatibility in the messaging on the wire, which - * saw a few breaking changes. At the time of writing, this stack - * supports MoQ versions from v11 (\ref IMQUIC_MOQ_VERSION_11) up to v16 - * (\ref IMQUIC_MOQ_VERSION_16), but not all versions will be supported + * WebTransort protocol. At the time of writing, this stack + * supports MoQ versions from v16 (\ref IMQUIC_MOQ_VERSION_16) up to v17 + * (\ref IMQUIC_MOQ_VERSION_17), but not all versions will be supported * forever. It should also be pointed out that not all features of all * versions are currently supported, so there may be some missing functionality * depending on which version you decide to negotiate. The \ref IMQUIC_MOQ_VERSION_MIN @@ -304,6 +300,29 @@ /** @name MoQ resources */ ///@{ +/*! \brief Versions that can be negotiated */ +typedef enum imquic_moq_version { + /* Base */ + IMQUIC_MOQ_VERSION_BASE = 0xff000000, + /* Draft version -16 */ + IMQUIC_MOQ_VERSION_MIN = 0xff000010, + IMQUIC_MOQ_VERSION_16 = 0xff000010, + /* Draft version -17 */ + IMQUIC_MOQ_VERSION_17 = 0xff000011, + IMQUIC_MOQ_VERSION_MAX = IMQUIC_MOQ_VERSION_17, + /* Any version starting from v15: for client, it means offer all supported versions; + * for servers, it means accept the first supported offered version */ + IMQUIC_MOQ_VERSION_ANY = 0xff0000ff, +} imquic_moq_version; +/*! \brief Helper function to serialize to string the name of a imquic_moq_version property. + * @param version The imquic_moq_version property + * @returns The version name as a string, if valid, or NULL otherwise */ +const char *imquic_moq_version_str(imquic_moq_version version); +/*! \brief Helper function to get the MoQ version associated with a connection. + * @param conn The imquic_connection to query + * @returns The imquic_moq_version value */ +imquic_moq_version imquic_moq_get_version(imquic_connection *conn); + /*! \brief MoQ Track Namespace */ typedef struct imquic_moq_namespace { /*! \brief Namespace data (typically a non-null terminated string) */ @@ -410,8 +429,7 @@ typedef struct imquic_moq_subscription_filter { uint64_t end_group; } imquic_moq_subscription_filter; -/*! \brief Subscribe options for namespaces - * \note Only supported since version -16 of the protocol */ +/*! \brief Subscribe options for namespaces */ typedef enum imquic_moq_subscribe_namespace_options { IMQUIC_MOQ_WANT_PUBLISH = 0x0, IMQUIC_MOQ_WANT_NAMESPACE = 0x1, @@ -423,10 +441,7 @@ typedef enum imquic_moq_subscribe_namespace_options { const char *imquic_moq_subscribe_namespace_options_str(imquic_moq_subscribe_namespace_options type); /*! \brief MoQ request parameters - * \note This struct is used in the MoQ API signatures even when the - * negotiated version of MoQ is lower than v15, that is when most of - * the properties below were fields in the messages themselves, rather - * than parameters as they are now. The library takes care of the + * \note The library takes care of the * serialization/deserialization process automatically, so you can * refer to this struct from an application perspective, and the * library will take care of the rest. Notice that not all of these @@ -444,18 +459,10 @@ typedef struct imquic_moq_request_parameters { gboolean delivery_timeout_set; /*! \brief Value of the DELIVERY_TIMEOUT parameter */ uint64_t delivery_timeout; - /*! \brief Whether the MAX_CACHE_DURATION parameter is set - * \note Deprecated in v16, and moved to Track Extensions */ - gboolean max_cache_duration_set; - /*! \brief Value of the MAX_CACHE_DURATION parameter - * \note Deprecated in v16, and moved to Track Extensions */ - uint64_t max_cache_duration; - /*! \brief Whether the PUBLISHER_PRIORITY parameter is set - * \note Deprecated in v16, and moved to Track Extensions */ - gboolean publisher_priority_set; - /*! \brief Value of the PUBLISHER_PRIORITY parameter - * \note Deprecated in v16, and moved to Track Extensions */ - uint8_t publisher_priority; + /*! \brief Whether the RENDEZVOUS_TIMEOUT parameter is set */ + gboolean rendezvous_timeout_set; + /*! \brief Value of the RENDEZVOUS_TIMEOUT parameter */ + uint64_t rendezvous_timeout; /*! \brief Whether the SUBSCRIBER_PRIORITY parameter is set */ gboolean subscriber_priority_set; /*! \brief Value of the SUBSCRIBER_PRIORITY parameter */ @@ -480,12 +487,6 @@ typedef struct imquic_moq_request_parameters { gboolean forward_set; /*! \brief Value of the FORWARD parameter */ gboolean forward; - /*! \brief Whether the DYNAMIC_GROUPS parameter is set - * \note Deprecated in v16, and moved to Track Extensions */ - gboolean dynamic_groups_set; - /*! \brief Value of the DYNAMIC_GROUPS parameter - * \note Deprecated in v16, and moved to Track Extensions */ - gboolean dynamic_groups; /*! \brief Whether the NEW_GROUP_REQUEST parameter is set */ gboolean new_group_request_set; /*! \brief Value of the NEW_GROUP_REQUEST parameter */ @@ -519,8 +520,6 @@ const char *imquic_moq_delivery_str(imquic_moq_delivery type); typedef enum imquic_moq_object_status { /*! \brief Normal object */ IMQUIC_MOQ_NORMAL_OBJECT = 0x0, - /*! \brief Object doesn't exist (deprecated in v16) */ - IMQUIC_MOQ_OBJECT_DOESNT_EXIST = 0x1, /*! \brief End of group */ IMQUIC_MOQ_END_OF_GROUP = 0x3, /*! \brief End of track */ @@ -531,49 +530,49 @@ typedef enum imquic_moq_object_status { * @returns The type name as a string, if valid, or NULL otherwise */ const char *imquic_moq_object_status_str(imquic_moq_object_status status); -/*! \brief MoQ Object Extension +/*! \brief MoQ Property * \note This may contain info related to different MoQ versions, and so * should be considered a higher level abstraction that the internal * MoQ stack may (and often will) use and notify differently */ -typedef struct imquic_moq_object_extension { - /*! \brief MoQ extension ID */ +typedef struct imquic_moq_property { + /*! \brief MoQ Property ID */ uint32_t id; - /*! \brief Extension value, which could be either a number (even - * extension ID) or an octet of data with length (odd extension ID) */ + /*! \brief Property value, which could be either a number (even + * property ID) or an octet of data with length (odd property ID) */ union { uint64_t number; - struct imquic_moq_object_extension_data { + struct imquic_moq_property_data { uint64_t length; uint8_t *buffer; } data; } value; -} imquic_moq_object_extension; +} imquic_moq_property; -/*! \brief Known MoQ Object Extension header types - * \note The library will not try to interpret extensions and their +/*! \brief Known MoQ Property types + * \note The library will not try to interpret properties and their * payload: this is always left up to applications */ -typedef enum imquic_moq_extension_type { - /* Delivery Timeout (added in v16) */ - IMQUIC_MOQ_EXT_DELIVERY_TIMEOUT = 0x02, - /* Max Cache Duration (added in v16) */ - IMQUIC_MOQ_EXT_MAX_CACHE_DURATION = 0x04, - /* Default Publisher Priority (added in v16) */ - IMQUIC_MOQ_EXT_DEFAULT_PUBLISHER_PRIORITY = 0x0E, - /* Default Group Order (added in v16) */ - IMQUIC_MOQ_EXT_DEFAULT_GROUP_ORDER = 0x22, - /* Dynamic Groups (added in v16) */ - IMQUIC_MOQ_EXT_DYNAMIC_GROUPS = 0x30, +typedef enum imquic_moq_property_type { + /* Delivery Timeout */ + IMQUIC_MOQ_PROPERTY_DELIVERY_TIMEOUT = 0x02, + /* Max Cache Duration */ + IMQUIC_MOQ_PROPERTY_MAX_CACHE_DURATION = 0x04, + /* Default Publisher Priority */ + IMQUIC_MOQ_PROPERTY_DEFAULT_PUBLISHER_PRIORITY = 0x0E, + /* Default Group Order */ + IMQUIC_MOQ_PROPERTY_DEFAULT_GROUP_ORDER = 0x22, + /* Dynamic Groups */ + IMQUIC_MOQ_PROPERTY_DYNAMIC_GROUPS = 0x30, /* Prior Group ID Gap */ - IMQUIC_MOQ_EXT_PRIOR_GROUP_ID_GAP = 0x3C, + IMQUIC_MOQ_PROPERTY_PRIOR_GROUP_ID_GAP = 0x3C, /* Prior Object ID Gap */ - IMQUIC_MOQ_EXT_PRIOR_OBJECT_ID_GAP = 0x3E, - /* Immutable Extensions */ - IMQUIC_MOQ_EXT_IMMUTABLE_EXTENSIONS = 0xB, -} imquic_moq_extension_type; -/*! \brief Helper function to serialize to string the name of a imquic_moq_extension_type value. - * @param type The imquic_moq_extension_type value + IMQUIC_MOQ_PROPERTY_PRIOR_OBJECT_ID_GAP = 0x3E, + /* Immutable Properties */ + IMQUIC_MOQ_PROPERTY_IMMUTABLE_PROPERTIES = 0xB, +} imquic_moq_property_type; +/*! \brief Helper function to serialize to string the name of a imquic_moq_property_type value. + * @param type The imquic_moq_property_type value * @returns The type name as a string, if valid, or NULL otherwise */ -const char *imquic_moq_extension_type_str(imquic_moq_extension_type type); +const char *imquic_moq_property_type_str(imquic_moq_property_type type); /*! \brief MoQ Object * \note This may contain info related to different MoQ versions, and so @@ -598,8 +597,8 @@ typedef struct imquic_moq_object { uint8_t *payload; /*! \brief Size of the MoQ object payload */ size_t payload_len; - /*! \brief MoQ object extensions, if any */ - GList *extensions; + /*! \brief MoQ properties, if any */ + GList *properties; /*! \brief How to send this object (or how it was received) */ imquic_moq_delivery delivery; /*! \brief Whether this signals the end of the stream */ @@ -645,17 +644,19 @@ typedef struct imquic_moq_auth_token { * @note The buffer in the \c value property will point to data in the original \c bytes buffer, * which means that no allocation will be performed by this method. If you need to store the * token value somewhere, it's up to you to copy it before \c bytes is invalidated by the application + * @param[in] version The MoQ version used on the connection * @param[in] bytes The buffer containing the auth token data * @param[in] blen The size of the buffer containing the auth token data data * @param[out] token The imquic_moq_auth_token to put the parsed token info to * @returns 0 in case of success, or a negative integer otherwise */ -int imquic_moq_parse_auth_token(uint8_t *bytes, size_t blen, imquic_moq_auth_token *token); +int imquic_moq_parse_auth_token(imquic_moq_version version, uint8_t *bytes, size_t blen, imquic_moq_auth_token *token); /*! \brief Helper mode to craft an auth token buffer out of a imquic_moq_auth_token instance + * @param[in] version The MoQ version used on the connection * @param[in] token The imquic_moq_auth_token instance to serialize * @param[out] bytes The buffer to write the auth token to * @param[in] blen The size of the buffer to write to * @returns How many bytes were written, if successful */ -size_t imquic_moq_build_auth_token(imquic_moq_auth_token *token, uint8_t *bytes, size_t blen); +size_t imquic_moq_build_auth_token(imquic_moq_version version, imquic_moq_auth_token *token, uint8_t *bytes, size_t blen); /** @name MoQ error and status codes */ @@ -669,7 +670,8 @@ typedef enum imquic_moq_error_code { IMQUIC_MOQ_INVALID_REQUEST_ID = 0x4, IMQUIC_MOQ_DUPLICATE_TRACK_ALIAS = 0x5, IMQUIC_MOQ_KEYVALUE_FORMATTING_ERROR = 0x6, - IMQUIC_MOQ_TOO_MANY_REQUESTS = 0x7, + IMQUIC_MOQ_INVALID_REQUIRED_REQUEST_ID = 0x7, + IMQUIC_MOQ_TOO_MANY_REQUESTS = 0x7, /* Deprecated in v17 */ IMQUIC_MOQ_INVALID_PATH = 0x8, IMQUIC_MOQ_MALFORMED_PATH = 0x9, IMQUIC_MOQ_GOAWAY_TIMEOUT = 0x10, @@ -699,6 +701,8 @@ typedef enum imquic_moq_request_error_code { IMQUIC_MOQ_REQERR_NOT_SUPPORTED = 0x3, IMQUIC_MOQ_REQERR_MALFORMED_AUTH_TOKEN = 0x4, IMQUIC_MOQ_REQERR_EXPIRED_AUTH_TOKEN = 0x5, + IMQUIC_MOQ_REQERR_GOING_AWAY = 0x6, + IMQUIC_MOQ_REQERR_EXCESSIVE_LOAD = 0x9, /* The following are returned by publishers */ IMQUIC_MOQ_REQERR_DOES_NOT_EXIST = 0x10, IMQUIC_MOQ_REQERR_INVALID_RANGE = 0x11, @@ -708,8 +712,8 @@ typedef enum imquic_moq_request_error_code { IMQUIC_MOQ_REQERR_UNINTERESTED = 0x20, /* Others */ IMQUIC_MOQ_REQERR_PREFIX_OVERLAP = 0x30, + IMQUIC_MOQ_REQERR_NAMESPACE_TOO_LARGE = 0x31, IMQUIC_MOQ_REQERR_INVALID_JOINING_REQUEST_ID = 0x32, - IMQUIC_MOQ_REQERR_UNKNOWN_STATUS_IN_RANGE = 0x33, /* Deprecated in v16 */ } imquic_moq_request_error_code; /*! \brief Helper function to serialize to string the name of a imquic_moq_request_error_code value. * @param code The imquic_moq_request_error_code value @@ -725,8 +729,9 @@ typedef enum imquic_moq_pub_done_code { IMQUIC_MOQ_PUBDONE_GOING_AWAY = 0x4, IMQUIC_MOQ_PUBDONE_EXPIRED = 0x5, IMQUIC_MOQ_PUBDONE_TOO_FAR_BEHIND = 0x6, - IMQUIC_MOQ_PUBDONE_MALFORMED_TRACK = 0x12, IMQUIC_MOQ_PUBDONE_UPDATE_FAILED = 0x8, + IMQUIC_MOQ_PUBDONE_EXCESSIVE_LOAD = 0x9, + IMQUIC_MOQ_PUBDONE_MALFORMED_TRACK = 0x12, } imquic_moq_pub_done_code; /*! \brief Helper function to serialize to string the name of a imquic_moq_pub_done_code value. * @param code The imquic_moq_pub_done_code value @@ -740,6 +745,8 @@ typedef enum imquic_moq_reset_stream_code { IMQUIC_MOQ_RESET_DELIVERY_TIMEOUT = 0x2, IMQUIC_MOQ_RESET_SESSION_CLOSED = 0x3, IMQUIC_MOQ_RESET_UNKNOWN_OBJECT_STATUS = 0x4, + IMQUIC_MOQ_RESET_TOO_FAR_BEHIND = 0x5, + IMQUIC_MOQ_RESET_EXCESSIVE_LOAD = 0x9, IMQUIC_MOQ_RESET_MALFORMED_TRACK = 0x12, } imquic_moq_reset_stream_code; /*! \brief Helper function to serialize to string the name of a imquic_moq_reset_stream_code value. @@ -763,7 +770,7 @@ const char *imquic_moq_reset_stream_code_str(imquic_moq_reset_stream_code code); IMQUIC_CONFIG_TLS_PASSWORD, cert_pwd, IMQUIC_CONFIG_LOCAL_PORT, 9000, IMQUIC_CONFIG_WEBTRANSPORT, TRUE, - IMQUIC_CONFIG_MOQ_VERSION, IMQUIC_MOQ_VERSION_16, + IMQUIC_CONFIG_MOQ_VERSION, IMQUIC_MOQ_VERSION_17, IMQUIC_CONFIG_DONE, NULL); \endverbatim * to create a QUIC server that will automatically negotiate MoQ over @@ -796,7 +803,7 @@ imquic_server *imquic_create_moq_server(const char *name, ...); IMQUIC_CONFIG_REMOTE_HOST, "127.0.0.1", IMQUIC_CONFIG_REMOTE_PORT, 9000, IMQUIC_CONFIG_WEBTRANSPORT, TRUE, - IMQUIC_CONFIG_MOQ_VERSION, IMQUIC_MOQ_VERSION_16, + IMQUIC_CONFIG_MOQ_VERSION, IMQUIC_MOQ_VERSION_17, IMQUIC_CONFIG_HTTP3_PATH, "/moq", IMQUIC_CONFIG_DONE, NULL); @@ -853,13 +860,17 @@ void imquic_set_moq_ready_cb(imquic_endpoint *endpoint, * @param endpoint The imquic_endpoint (imquic_server or imquic_client) to configure * @param incoming_publish_namespace Pointer to the function that will handle the incoming \c PUBLISH_NAMESPACE */ void imquic_set_incoming_publish_namespace_cb(imquic_endpoint *endpoint, - void (* incoming_publish_namespace)(imquic_connection *conn, uint64_t request_id, imquic_moq_namespace *tns, imquic_moq_request_parameters *parameters)); + void (* incoming_publish_namespace)(imquic_connection *conn, uint64_t request_id, uint64_t required_id_delta, imquic_moq_namespace *tns, imquic_moq_request_parameters *parameters)); /*! \brief Configure the callback function to be notified when there's * an incoming \c PUBLISH_NAMESPACE_CANCEL request. + * \note Starting in v17, \c PUBLISH_NAMESPACE_CANCEL doesn't exist anymore, + * so this callback is only fired if the endpoint that sent the + * \c PUBLISH_NAMESPACE closed the associated bidirectiomal stream before + * getting a \c REQUEST_OK or \c REQUEST_ERROR back. * @param endpoint The imquic_endpoint (imquic_server or imquic_client) to configure * @param incoming_publish_namespace_cancel Pointer to the function that will handle the incoming \c PUBLISH_NAMESPACE_CANCEL */ void imquic_set_incoming_publish_namespace_cancel_cb(imquic_endpoint *endpoint, - void (* incoming_publish_namespace_cancel)(imquic_connection *conn, imquic_moq_namespace *tns, imquic_moq_request_error_code error_code, const char *reason)); + void (* incoming_publish_namespace_cancel)(imquic_connection *conn, uint64_t request_id, imquic_moq_request_error_code error_code, const char *reason)); /*! \brief Configure the callback function to be notified when an * \c PUBLISH_NAMESPACE we previously sent was accepted * @param endpoint The imquic_endpoint (imquic_server or imquic_client) to configure @@ -874,17 +885,21 @@ void imquic_set_publish_namespace_error_cb(imquic_endpoint *endpoint, void (* publish_namespace_error)(imquic_connection *conn, uint64_t request_id, imquic_moq_request_error_code error_code, const char *reason, uint64_t retry_interval)); /*! \brief Configure the callback function to be notified when there's * an incoming \c PUBLISH_NAMESPACE_DONE request. + * \note Starting in v17, \c PUBLISH_NAMESPACE_DONE doesn't exist anymore, + * so this callback is only fired if the endpoint that sent the + * \c PUBLISH_NAMESPACE closed the associated bidirectiomal stream after + * getting a \c REQUEST_OK or \c REQUEST_ERROR back. * @param endpoint The imquic_endpoint (imquic_server or imquic_client) to configure * @param publish_namespace_done Pointer to the function that will handle the incoming \c PUBLISH_NAMESPACE_DONE */ void imquic_set_publish_namespace_done_cb(imquic_endpoint *endpoint, - void (* publish_namespace_done)(imquic_connection *conn, imquic_moq_namespace *tns)); + void (* publish_namespace_done)(imquic_connection *conn, uint64_t request_id)); /*! \brief Configure the callback function to be notified when there's * an incoming \c PUBLISH request. * @param endpoint The imquic_endpoint (imquic_server or imquic_client) to configure * @param incoming_publish Pointer to the function that will handle the incoming \c PUBLISH */ void imquic_set_incoming_publish_cb(imquic_endpoint *endpoint, - void (* incoming_publish)(imquic_connection *conn, uint64_t request_id, imquic_moq_namespace *tns, imquic_moq_name *tn, - uint64_t track_alias, imquic_moq_request_parameters *parameters, GList *track_extensions)); + void (* incoming_publish)(imquic_connection *conn, uint64_t request_id, uint64_t required_id_delta, imquic_moq_namespace *tns, imquic_moq_name *tn, + uint64_t track_alias, imquic_moq_request_parameters *parameters, GList *track_properties)); /*! \brief Configure the callback function to be notified when a * \c PUBLISH we previously sent was accepted * @param endpoint The imquic_endpoint (imquic_server or imquic_client) to configure @@ -902,29 +917,28 @@ void imquic_set_publish_error_cb(imquic_endpoint *endpoint, * @param endpoint The imquic_endpoint (imquic_server or imquic_client) to configure * @param incoming_subscribe Pointer to the function that will handle the incoming \c SUBSCRIBE */ void imquic_set_incoming_subscribe_cb(imquic_endpoint *endpoint, - void (* incoming_subscribe)(imquic_connection *conn, uint64_t request_id, uint64_t track_alias, + void (* incoming_subscribe)(imquic_connection *conn, uint64_t request_id, uint64_t required_id_delta, imquic_moq_namespace *tns, imquic_moq_name *tn, imquic_moq_request_parameters *parameters)); /*! \brief Configure the callback function to be notified when a * \c SUBSCRIBE we previously sent was accepted * @param endpoint The imquic_endpoint (imquic_server or imquic_client) to configure * @param subscribe_accepted Pointer to the function that will fire when a \c SUBSCRIBE is accepted */ void imquic_set_subscribe_accepted_cb(imquic_endpoint *endpoint, - void (* subscribe_accepted)(imquic_connection *conn, uint64_t request_id, uint64_t track_alias, imquic_moq_request_parameters *parameters, GList *track_extensions)); + void (* subscribe_accepted)(imquic_connection *conn, uint64_t request_id, uint64_t track_alias, imquic_moq_request_parameters *parameters, GList *track_properties)); /*! \brief Configure the callback function to be notified when a * \c SUBSCRIBE we previously sent was rejected with an error * @param endpoint The imquic_endpoint (imquic_server or imquic_client) to configure * @param subscribe_error Pointer to the function that will fire when a \c SUBSCRIBE is rejected */ void imquic_set_subscribe_error_cb(imquic_endpoint *endpoint, - void (* subscribe_error)(imquic_connection *conn, uint64_t request_id, imquic_moq_request_error_code error_code, const char *reason, uint64_t track_alias, uint64_t retry_interval)); + void (* subscribe_error)(imquic_connection *conn, uint64_t request_id, imquic_moq_request_error_code error_code, const char *reason, uint64_t retry_interval)); /*! \brief Configure the callback function to be notified when an update * is received for a request (e.g., a \c SUBSCRIBE ) we previously sent * @param endpoint The imquic_endpoint (imquic_server or imquic_client) to configure * @param request_updated Pointer to the function that will fire when a \c SUBSCRIBE is done */ void imquic_set_request_updated_cb(imquic_endpoint *endpoint, - void (* request_updated)(imquic_connection *conn, uint64_t request_id, uint64_t sub_request_id, imquic_moq_request_parameters *parameters)); + void (* request_updated)(imquic_connection *conn, uint64_t request_id, uint64_t sub_request_id, uint64_t required_id_delta, imquic_moq_request_parameters *parameters)); /*! \brief Configure the callback function to be notified when an OK * is received for a \c REQUEST_UPDATE we previously sent - * @note This was only added in v15, and so will never be fired on older versions. * @param endpoint The imquic_endpoint (imquic_server or imquic_client) to configure * @param request_update_accepted Pointer to the function that will fire when a \c REQUEST_UPDATE is acknowledged */ void imquic_set_request_update_accepted_cb(imquic_endpoint *endpoint, @@ -943,12 +957,16 @@ void imquic_set_publish_done_cb(imquic_endpoint *endpoint, void (* publish_done)(imquic_connection *conn, uint64_t request_id, imquic_moq_pub_done_code status_code, uint64_t streams_count, const char *reason)); /*! \brief Configure the callback function to be notified when there's * an incoming \c UNSUBSCRIBE request. + * \note Starting in v17, \c UNSUBSCRIBE doesn't exist anymore, + * so this callback is only fired if the endpoint that sent the + * \c SUBSCRIBE closed the associated bidirectiomal stream. * @param endpoint The imquic_endpoint (imquic_server or imquic_client) to configure * @param incoming_unsubscribe Pointer to the function that will handle the incoming \c UNSUBSCRIBE */ void imquic_set_incoming_unsubscribe_cb(imquic_endpoint *endpoint, void (* incoming_unsubscribe)(imquic_connection *conn, uint64_t request_id)); /*! \brief Configure the callback function to be notified when there's * an incoming \c REQUESTS_BLOCKED request. + * \note Deprecated in v17 * @param endpoint The imquic_endpoint (imquic_server or imquic_client) to configure * @param requests_blocked Pointer to the function that will handle the incoming \c REQUESTS_BLOCKED */ void imquic_set_requests_blocked_cb(imquic_endpoint *endpoint, @@ -958,7 +976,7 @@ void imquic_set_requests_blocked_cb(imquic_endpoint *endpoint, * @param endpoint The imquic_endpoint (imquic_server or imquic_client) to configure * @param incoming_subscribe_namespace Pointer to the function that will handle the incoming \c SUBSCRIBE_NAMESPACE */ void imquic_set_incoming_subscribe_namespace_cb(imquic_endpoint *endpoint, - void (* incoming_subscribe_namespace)(imquic_connection *conn, uint64_t request_id, + void (* incoming_subscribe_namespace)(imquic_connection *conn, uint64_t request_id, uint64_t required_id_delta, imquic_moq_namespace *tns, imquic_moq_subscribe_namespace_options subscribe_options, imquic_moq_request_parameters *parameters)); /*! \brief Configure the callback function to be notified when an * \c SUBSCRIBE_NAMESPACE we previously sent was accepted @@ -974,10 +992,13 @@ void imquic_set_subscribe_namespace_error_cb(imquic_endpoint *endpoint, void (* subscribe_namespace_error)(imquic_connection *conn, uint64_t request_id, imquic_moq_request_error_code error_code, const char *reason, uint64_t retry_interval)); /*! \brief Configure the callback function to be notified when there's * an incoming \c UNSUBSCRIBE_NAMESPACE request. + * \note On newer versions, \c UNSUBSCRIBE_NAMESPACE doesn't exist anymore, + * so this callback is only fired if the endpoint that sent the + * \c SUBSCRIBE_NAMESPACE closed the associated bidirectiomal stream. * @param endpoint The imquic_endpoint (imquic_server or imquic_client) to configure * @param incoming_unsubscribe_namespace Pointer to the function that will handle the incoming \c UNSUBSCRIBE_NAMESPACE */ void imquic_set_incoming_unsubscribe_namespace_cb(imquic_endpoint *endpoint, - void (* incoming_unsubscribe_namespace)(imquic_connection *conn, uint64_t request_id, imquic_moq_namespace *tns)); + void (* incoming_unsubscribe_namespace)(imquic_connection *conn, uint64_t request_id)); /*! \brief Configure the callback function to be notified when there's * an incoming \c NAMESPACE request. * @param endpoint The imquic_endpoint (imquic_server or imquic_client) to configure @@ -990,22 +1011,31 @@ void imquic_set_incoming_namespace_cb(imquic_endpoint *endpoint, * @param incoming_namespace_done Pointer to the function that will handle the incoming \c NAMESPACE_DONE */ void imquic_set_incoming_namespace_done_cb(imquic_endpoint *endpoint, void (* incoming_namespace_done)(imquic_connection *conn, uint64_t request_id, imquic_moq_namespace *tns)); +/*! \brief Configure the callback function to be notified when there's + * an incoming \c PUBLISH_BLOCKED request. + * @param endpoint The imquic_endpoint (imquic_server or imquic_client) to configure + * @param incoming_publish_blocked Pointer to the function that will handle the incoming \c PUBLISH_BLOCKED */ +void imquic_set_incoming_publish_blocked_cb(imquic_endpoint *endpoint, + void (* incoming_publish_blocked)(imquic_connection *conn, uint64_t request_id, imquic_moq_namespace *tns, imquic_moq_name *tn)); /*! \brief Configure the callback function to be notified when there's * an incoming standalone \c FETCH request. * @param endpoint The imquic_endpoint (imquic_server or imquic_client) to configure * @param incoming_standalone_fetch Pointer to the function that will handle the incoming \c FETCH */ void imquic_set_incoming_standalone_fetch_cb(imquic_endpoint *endpoint, - void (* incoming_standalone_fetch)(imquic_connection *conn, uint64_t request_id, + void (* incoming_standalone_fetch)(imquic_connection *conn, uint64_t request_id, uint64_t required_id_delta, imquic_moq_namespace *tns, imquic_moq_name *tn, imquic_moq_location_range *range, imquic_moq_request_parameters *parameters)); /*! \brief Configure the callback function to be notified when there's * an incoming joining \c FETCH request. * @param endpoint The imquic_endpoint (imquic_server or imquic_client) to configure * @param incoming_joining_fetch Pointer to the function that will handle the incoming \c FETCH */ void imquic_set_incoming_joining_fetch_cb(imquic_endpoint *endpoint, - void (* incoming_joining_fetch)(imquic_connection *conn, uint64_t request_id, uint64_t joining_request_id, + void (* incoming_joining_fetch)(imquic_connection *conn, uint64_t request_id, uint64_t required_id_delta, uint64_t joining_request_id, gboolean absolute, uint64_t joining_start, imquic_moq_request_parameters *parameters)); /*! \brief Configure the callback function to be notified when there's * an incoming \c FETCH_CANCEL request. + * \note Starting in v17, \c FETCH_CANCEL doesn't exist anymore, + * so this callback is only fired if the endpoint that sent the + * \c FETCH closed the associated bidirectiomal stream. * @param endpoint The imquic_endpoint (imquic_server or imquic_client) to configure * @param incoming_fetch_cancel Pointer to the function that will handle the incoming \c FETCH_CANCEL */ void imquic_set_incoming_fetch_cancel_cb(imquic_endpoint *endpoint, @@ -1016,7 +1046,7 @@ void imquic_set_incoming_fetch_cancel_cb(imquic_endpoint *endpoint, * @param fetch_accepted Pointer to the function that will fire when an \c FETCH is accepted */ void imquic_set_fetch_accepted_cb(imquic_endpoint *endpoint, void (* fetch_accepted)(imquic_connection *conn, uint64_t request_id, - imquic_moq_location *largest, imquic_moq_request_parameters *parameters, GList *track_extensions)); + imquic_moq_location *largest, imquic_moq_request_parameters *parameters, GList *track_properties)); /*! \brief Configure the callback function to be notified when an * \c FETCH we previously sent was rejected with an error * @param endpoint The imquic_endpoint (imquic_server or imquic_client) to configure @@ -1035,7 +1065,7 @@ void imquic_set_incoming_track_status_cb(imquic_endpoint *endpoint, * @param endpoint The imquic_endpoint (imquic_server or imquic_client) to configure * @param track_status_accepted Pointer to the function that will fire when a \c TRACK_STATUS is accepted */ void imquic_set_track_status_accepted_cb(imquic_endpoint *endpoint, - void (* track_status_accepted)(imquic_connection *conn, uint64_t request_id, uint64_t track_alias, imquic_moq_request_parameters *parameters)); + void (* track_status_accepted)(imquic_connection *conn, uint64_t request_id, imquic_moq_request_parameters *parameters)); /*! \brief Configure the callback function to be notified when a * \c TRACK_STATUS we previously sent was rejected with an error * @param endpoint The imquic_endpoint (imquic_server or imquic_client) to configure @@ -1053,7 +1083,7 @@ void imquic_set_incoming_object_cb(imquic_endpoint *endpoint, * @param endpoint The imquic_endpoint (imquic_server or imquic_client) to configure * @param incoming_goaway Pointer to the function that will handle the incoming \c GOAWAY */ void imquic_set_incoming_goaway_cb(imquic_endpoint *endpoint, - void (* incoming_goaway)(imquic_connection *conn, const char *uri)); + void (* incoming_goaway)(imquic_connection *conn, const char *uri, uint64_t timeout)); /*! \brief Configure the callback function to be notified when an existing * MoQ connection handled by this endpoint has been closed/shut down. * @note This is a good place to release the last reference to the connection @@ -1063,40 +1093,6 @@ void imquic_set_moq_connection_gone_cb(imquic_endpoint *endpoint, void (* moq_connection_gone)(imquic_connection *conn)); ///@} -/*! \brief Versions that can be negotiated */ -typedef enum imquic_moq_version { - /* Base */ - IMQUIC_MOQ_VERSION_BASE = 0xff000000, - /* Draft version -11 */ - IMQUIC_MOQ_VERSION_MIN = 0xff00000B, - IMQUIC_MOQ_VERSION_11 = 0xff00000B, - /* Draft version -12 */ - IMQUIC_MOQ_VERSION_12 = 0xff00000C, - /* Draft version -13 */ - IMQUIC_MOQ_VERSION_13 = 0xff00000D, - /* Draft version -14 */ - IMQUIC_MOQ_VERSION_14 = 0xff00000E, - /* Draft version -15 */ - IMQUIC_MOQ_VERSION_15 = 0xff00000F, - /* Draft version -16 */ - IMQUIC_MOQ_VERSION_16 = 0xff000010, - IMQUIC_MOQ_VERSION_MAX = IMQUIC_MOQ_VERSION_16, - /* Any version starting from v15: for client, it means offer all supported versions; - * for servers, it means accept the first supported offered version */ - IMQUIC_MOQ_VERSION_ANY = 0xff0000ff, - /* Any version between v11 and v14: for client, it means offer all those versions; - * for servers, it means accept the first supported offered version */ - IMQUIC_MOQ_VERSION_ANY_LEGACY = 0xff0000fe -} imquic_moq_version; -/*! \brief Helper function to serialize to string the name of a imquic_moq_version property. - * @param version The imquic_moq_version property - * @returns The version name as a string, if valid, or NULL otherwise */ -const char *imquic_moq_version_str(imquic_moq_version version); -/*! \brief Helper function to get the MoQ version associated with a connection. - * @param conn The imquic_connection to query - * @returns The imquic_moq_version value */ -imquic_moq_version imquic_moq_get_version(imquic_connection *conn); - /*! \brief Method to provide credentials, as a client, on a new connection. * If credentials need to provided, this must be done as soon as the * connection is established, and before sending any MoQ message. @@ -1111,7 +1107,8 @@ imquic_moq_version imquic_moq_get_version(imquic_connection *conn); int imquic_moq_set_connection_auth(imquic_connection *conn, uint8_t *auth, size_t authlen); /*! \brief Helper function to set the Maximum Request ID a subscriber can send - * \note If invoked before the MoQ connection setup, it will be put in the + * \note Deprecated in v17, and so not needed anymore. For older versions, + * if invoked before the MoQ connection setup, it will be put in the * setup parameter, otherwise it's sent in a \c MAX_REQUEST_ID request. * Notice that whatever is passed to the request will be decremented by * 1, as per the specification, meaning you cannot pass \c 0 as a value here @@ -1140,15 +1137,16 @@ const char *imquic_moq_get_remote_implementation(imquic_connection *conn); /*! \brief Function to send a \c PUBLISH_NAMESPACE request * @param conn The imquic_connection to send the request on * @param request_id A unique request ID + * @param required_id_delta Required Request ID Delta, if needed (ignored before v17) * @param tns The imquic_moq_namespace namespace to publish_namespace * @param parameters The parameters to add to the request * @returns 0 in case of success, a negative integer otherwise */ -int imquic_moq_publish_namespace(imquic_connection *conn, uint64_t request_id, imquic_moq_namespace *tns, - imquic_moq_request_parameters *parameters); +int imquic_moq_publish_namespace(imquic_connection *conn, uint64_t request_id, + uint64_t required_id_delta, imquic_moq_namespace *tns, imquic_moq_request_parameters *parameters); /*! \brief Function to accept an incoming \c PUBLISH_NAMESPACE request * @param conn The imquic_connection to send the request on * @param request_id The request ID of the original \c PUBLISH_NAMESPACE request - * @param parameters The parameters to add to the request (ignored before v15) + * @param parameters The parameters to add to the request * @returns 0 in case of success, a negative integer otherwise */ int imquic_moq_accept_publish_namespace(imquic_connection *conn, uint64_t request_id, imquic_moq_request_parameters *parameters); @@ -1157,27 +1155,29 @@ int imquic_moq_accept_publish_namespace(imquic_connection *conn, uint64_t reques * @param request_id The request ID of the original \c PUBLISH_NAMESPACE request * @param error_code The error code to send back * @param reason A string representation of the error, if needed - * @param retry_interval Retry interval in ms (added in v16, ignored otherwise) + * @param retry_interval Retry interval in ms * @returns 0 in case of success, a negative integer otherwise */ int imquic_moq_reject_publish_namespace(imquic_connection *conn, uint64_t request_id, imquic_moq_request_error_code error_code, const char *reason, uint64_t retry_interval); /*! \brief Function to send a \c PUBLISH_NAMESPACE_DONE request * @param conn The imquic_connection to send the request on - * @param tns The imquic_moq_namespace namespace to publish_namespace_done + * @param request_id The unique \c request_id value associated to the original \c PUBLISH_NAMESPACE * @returns 0 in case of success, a negative integer otherwise */ -int imquic_moq_publish_namespace_done(imquic_connection *conn, imquic_moq_namespace *tns); +int imquic_moq_publish_namespace_done(imquic_connection *conn, uint64_t request_id); /*! \brief Function to send a \c PUBLISH request * @param conn The imquic_connection to send the request on * @param request_id A unique request ID to associate to this subscription + * @param required_id_delta Required Request ID Delta, if needed (ignored before v17) * @param tns The imquic_moq_namespace namespace the track to publish to belongs to * @param tn The imquic_moq_name track name to publish to * @param track_alias A unique numeric identifier to associate to the track in this subscription * @param parameters The parameters to add to the request - * @param track_extensions List of track extensions to add, if any (added in v16) + * @param track_properties List of track properties to add, if any * @returns 0 in case of success, a negative integer otherwise */ -int imquic_moq_publish(imquic_connection *conn, uint64_t request_id, +int imquic_moq_publish(imquic_connection *conn, + uint64_t request_id, uint64_t required_id_delta, imquic_moq_namespace *tns, imquic_moq_name *tn, - uint64_t track_alias, imquic_moq_request_parameters *parameters, GList *track_extensions); + uint64_t track_alias, imquic_moq_request_parameters *parameters, GList *track_properties); /*! \brief Function to accept an incoming \c PUBLISH request * @param conn The imquic_connection to send the request on * @param request_id The unique \c request_id value associated to the subscription to accept @@ -1189,52 +1189,48 @@ int imquic_moq_accept_publish(imquic_connection *conn, uint64_t request_id, imqu * @param request_id The unique \c request_id value associated to the subscription to reject * @param error_code The error code to send back * @param reason A string representation of the error, if needed - * @param retry_interval Retry interval in ms (added in v16, ignored otherwise) + * @param retry_interval Retry interval in ms * @returns 0 in case of success, a negative integer otherwise */ int imquic_moq_reject_publish(imquic_connection *conn, uint64_t request_id, imquic_moq_request_error_code error_code, const char *reason, uint64_t retry_interval); /*! \brief Function to send a \c SUBSCRIBE request * @param conn The imquic_connection to send the request on * @param request_id A unique request ID to associate to this subscription - * @param track_alias A unique numeric identifier to associate to the track in this subscription (ignored starting from v12) + * @param required_id_delta Required Request ID Delta, if needed (ignored before v17) * @param tns The imquic_moq_namespace namespace the track to subscribe to belongs to * @param tn The imquic_moq_name track name to subscribe to * @param parameters The parameters to add to the request * @returns 0 in case of success, a negative integer otherwise */ -int imquic_moq_subscribe(imquic_connection *conn, uint64_t request_id, uint64_t track_alias, +int imquic_moq_subscribe(imquic_connection *conn, uint64_t request_id, uint64_t required_id_delta, imquic_moq_namespace *tns, imquic_moq_name *tn, imquic_moq_request_parameters *parameters); /*! \brief Function to accept an incoming \c SUBSCRIBE request * @param conn The imquic_connection to send the request on * @param request_id The unique \c request_id value associated to the subscription to accept - * @param track_alias The unique \c track_alias value associated to the subscription to accept (ignored before v12) + * @param track_alias The unique \c track_alias value associated to the subscription to accept * @param parameters The parameters to add to the request - * @param track_extensions List of track extensions to add, if any (added in v16) + * @param track_properties List of track properties to add, if any * @returns 0 in case of success, a negative integer otherwise */ int imquic_moq_accept_subscribe(imquic_connection *conn, uint64_t request_id, - uint64_t track_alias, imquic_moq_request_parameters *parameters, GList *track_extensions); + uint64_t track_alias, imquic_moq_request_parameters *parameters, GList *track_properties); /*! \brief Function to reject an incoming \c SUBSCRIBE request * @param conn The imquic_connection to send the request on * @param request_id The unique \c request_id value associated to the subscription to reject * @param error_code The error code to send back * @param reason A string representation of the error, if needed - * @param track_alias The unique \c track_alias value associated to the subscription to reject (ignored starting from v12) - * @param retry_interval Retry interval in ms (added in v16, ignored otherwise) + * @param retry_interval Retry interval in ms * @returns 0 in case of success, a negative integer otherwise */ int imquic_moq_reject_subscribe(imquic_connection *conn, uint64_t request_id, - imquic_moq_request_error_code error_code, const char *reason, uint64_t track_alias, uint64_t retry_interval); + imquic_moq_request_error_code error_code, const char *reason, uint64_t retry_interval); /*! \brief Function to send a \c REQUEST_UPDATE request - * \note Version 14 of the draft introduced a new "Subscription Request ID", which means - * the meaning of \c request_id will change depending on which version the connection is using. * @param conn The imquic_connection to send the request on - * @param request_id Unique \c request_id value (before v14, this is associated to the subscription to update) - * @param sub_request_id Unique \c request_id value associated to the subscription to update (ignored before v14) + * @param request_id Unique \c request_id value + * @param sub_request_id Unique \c request_id value associated to the subscription to update + * @param required_id_delta Required Request ID Delta, if needed (ignored before v17) * @param parameters The parameters to add to the request * @returns 0 in case of success, a negative integer otherwise */ int imquic_moq_update_request(imquic_connection *conn, uint64_t request_id, - uint64_t sub_request_id, imquic_moq_request_parameters *parameters); + uint64_t sub_request_id, uint64_t required_id_delta, imquic_moq_request_parameters *parameters); /*! \brief Function to accept an incoming \c REQUEST_UPDATE request - * \note This acknowledgement was only added in v15, which means it will - * be a no-action if you try to send it when negotiating an older version * @param conn The imquic_connection to send the request on * @param request_id The request ID of the original \c REQUEST_UPDATE request * @param parameters The parameters to add to the request @@ -1242,13 +1238,11 @@ int imquic_moq_update_request(imquic_connection *conn, uint64_t request_id, int imquic_moq_accept_request_update(imquic_connection *conn, uint64_t request_id, imquic_moq_request_parameters *parameters); /*! \brief Function to reject an incoming \c REQUEST_UPDATE request - * \note This error response was only added in v15, which means it will - * be a no-action if you try to send it when negotiating an older version * @param conn The imquic_connection to send the request on * @param request_id The unique \c request_id value associated to the subscription to reject * @param error_code The error code to send back * @param reason A string representation of the error, if needed - * @param retry_interval Retry interval in ms (added in v16, ignored otherwise) + * @param retry_interval Retry interval in ms * @returns 0 in case of success, a negative integer otherwise */ int imquic_moq_reject_request_update(imquic_connection *conn, uint64_t request_id, imquic_moq_request_error_code error_code, const char *reason, uint64_t retry_interval); @@ -1269,16 +1263,17 @@ int imquic_moq_unsubscribe(imquic_connection *conn, uint64_t request_id); /*! \brief Function to send a \c SUBSCRIBE_NAMESPACE request * @param conn The imquic_connection to send the request on * @param request_id A unique request ID + * @param required_id_delta Required Request ID Delta, if needed (ignored before v17) * @param tns The imquic_moq_namespace namespace the track to subscribe to belongs to - * @param subscribe_options The subscribe options to add to the request (added in v16, ignored otherwise) + * @param subscribe_options The subscribe options to add to the request * @param parameters The parameters to add to the request * @returns 0 in case of success, a negative integer otherwise */ -int imquic_moq_subscribe_namespace(imquic_connection *conn, uint64_t request_id, +int imquic_moq_subscribe_namespace(imquic_connection *conn, uint64_t request_id, uint64_t required_id_delta, imquic_moq_namespace *tns, imquic_moq_subscribe_namespace_options subscribe_options, imquic_moq_request_parameters *parameters); /*! \brief Function to accept an incoming \c SUBSCRIBE_NAMESPACE request * @param conn The imquic_connection to send the request on * @param request_id The request ID of the original \c SUBSCRIBE_NAMESPACE request - * @param parameters The parameters to add to the request (ignored before v15) + * @param parameters The parameters to add to the request * @returns 0 in case of success, a negative integer otherwise */ int imquic_moq_accept_subscribe_namespace(imquic_connection *conn, uint64_t request_id, imquic_moq_request_parameters *parameters); @@ -1287,7 +1282,7 @@ int imquic_moq_accept_subscribe_namespace(imquic_connection *conn, uint64_t requ * @param request_id The request ID of the original \c SUBSCRIBE_NAMESPACE request * @param error_code The error code to send back * @param reason A string representation of the error, if needed - * @param retry_interval Retry interval in ms (added in v16, ignored otherwise) + * @param retry_interval Retry interval in ms * @returns 0 in case of success, a negative integer otherwise */ int imquic_moq_reject_subscribe_namespace(imquic_connection *conn, uint64_t request_id, imquic_moq_request_error_code error_code, const char *reason, uint64_t retry_interval); @@ -1295,13 +1290,12 @@ int imquic_moq_reject_subscribe_namespace(imquic_connection *conn, uint64_t requ * \note Starting in v16, this doesn't actually send a request, but simply * closes the bidirectional STREAM that was created for the subscription * @param conn The imquic_connection to send the request on - * @param request_id The request ID of the original \c SUBSCRIBE_NAMESPACE request (added in v15, ignored otherwise) - * @param tns The imquic_moq_namespace namespace to unsubscribe notifications from (deprecated in v15) + * @param request_id The request ID of the original \c SUBSCRIBE_NAMESPACE request * @returns 0 in case of success, a negative integer otherwise */ -int imquic_moq_unsubscribe_namespace(imquic_connection *conn, uint64_t request_id, imquic_moq_namespace *tns); +int imquic_moq_unsubscribe_namespace(imquic_connection *conn, uint64_t request_id); /*! \brief Function to send a \c NAMESPACE request - * \note This was added in v16, and while the request itself doesn't contain - * the request ID, we use it to find the subscription and use the right STREAM + * \note While the request itself doesn't contain the request ID, we use + * it to find the subscription and use the right STREAM. * Notice the method expects the full track namespace: the stack will strip * the prefix itself, before sending the actual message. * @param conn The imquic_connection to send the request on @@ -1310,8 +1304,8 @@ int imquic_moq_unsubscribe_namespace(imquic_connection *conn, uint64_t request_i * @returns 0 in case of success, a negative integer otherwise */ int imquic_moq_notify_namespace(imquic_connection *conn, uint64_t request_id, imquic_moq_namespace *tns); /*! \brief Function to send a \c NAMESPACE_DONE request - * \note This was added in v16, and while the request itself doesn't contain - * the request ID, we use it to find the subscription and use the right STREAM. + * \note While the request itself doesn't contain the request ID, we use + * it to find the subscription and use the right STREAM. * Notice the method expects the full track namespace: the stack will strip * the prefix itself, before sending the actual message. * @param conn The imquic_connection to send the request on @@ -1319,54 +1313,68 @@ int imquic_moq_notify_namespace(imquic_connection *conn, uint64_t request_id, im * @param tns The imquic_moq_namespace namespace this request refers to * @returns 0 in case of success, a negative integer otherwise */ int imquic_moq_notify_namespace_done(imquic_connection *conn, uint64_t request_id, imquic_moq_namespace *tns); +/*! \brief Function to send a \c PUBLISH_BLOCKED request + * \note While the request itself doesn't contain the request ID, we use + * it to find the subscription and use the right STREAM. + * Notice the method expects the full track namespace: the stack will strip + * the prefix itself, before sending the actual message. + * @param conn The imquic_connection to send the request on + * @param request_id The request ID of the original \c SUBSCRIBE_NAMESPACE request + * @param tns The imquic_moq_namespace namespace this request refers to + * @param tn The imquic_moq_name track this request refers to + * @returns 0 in case of success, a negative integer otherwise */ +int imquic_moq_notify_publish_blocked(imquic_connection *conn, uint64_t request_id, imquic_moq_namespace *tns, imquic_moq_name *tn); /*! \brief Function to send a standalone \c FETCH request * @param conn The imquic_connection to send the request on * @param request_id A unique numeric identifier to associate to this subscription + * @param required_id_delta Required Request ID Delta, if needed (ignored before v17) * @param tns The imquic_moq_namespace namespace the track to fetch to belongs to * @param tn The imquic_moq_name track name to fetch to * @param range The range of groups/objects to fetch * @param parameters The parameters to add to the request * @returns 0 in case of success, a negative integer otherwise */ -int imquic_moq_standalone_fetch(imquic_connection *conn, uint64_t request_id, +int imquic_moq_standalone_fetch(imquic_connection *conn, + uint64_t request_id, uint64_t required_id_delta, imquic_moq_namespace *tns, imquic_moq_name *tn, imquic_moq_location_range *range, imquic_moq_request_parameters *parameters); /*! \brief Function to send a joining \c FETCH request * @param conn The imquic_connection to send the request on * @param request_id A unique numeric identifier to associate to this subscription + * @param required_id_delta Required Request ID Delta, if needed (ignored before v17) * @param joining_request_id Existing subscription to join * @param absolute Whether this is an absolute or relative joining \c FETCH * @param joining_start How many groups to retrieve before the current one, * for relative joins, or starting group ID for absolute joins * @param parameters The parameters to add to the request * @returns 0 in case of success, a negative integer otherwise */ -int imquic_moq_joining_fetch(imquic_connection *conn, uint64_t request_id, uint64_t joining_request_id, +int imquic_moq_joining_fetch(imquic_connection *conn, + uint64_t request_id, uint64_t required_id_delta, uint64_t joining_request_id, gboolean absolute, uint64_t joining_start, imquic_moq_request_parameters *parameters); /*! \brief Function to accept an incoming \c FETCH request * @param conn The imquic_connection to send the request on * @param request_id The unique \c request_id value associated to the subscription to accept * @param largest The largest group/object IDs * @param parameters The parameters to add to the request - * @param track_extensions List of track extensions to add, if any (added in v16) + * @param track_properties List of track properties to add, if any * @returns 0 in case of success, a negative integer otherwise */ int imquic_moq_accept_fetch(imquic_connection *conn, uint64_t request_id, - imquic_moq_location *largest, imquic_moq_request_parameters *parameters, GList *track_extensions); + imquic_moq_location *largest, imquic_moq_request_parameters *parameters, GList *track_properties); /*! \brief Function to reject an incoming \c FETCH request * @param conn The imquic_connection to send the request on * @param request_id The unique \c request_id value associated to the subscription to reject * @param error_code The error code to send back * @param reason A string representation of the error, if needed - * @param retry_interval Retry interval in ms (added in v16, ignored otherwise) + * @param retry_interval Retry interval in ms * @returns 0 in case of success, a negative integer otherwise */ int imquic_moq_reject_fetch(imquic_connection *conn, uint64_t request_id, imquic_moq_request_error_code error_code, const char *reason, uint64_t retry_interval); -/*! \brief Function to send a \c FETCH_CANCEL request +/*! \brief Function to send a \c FETCH_CANCEL request (legacy) or close + * the stream associated with the \c FETCH subscription (new draft versions) * @param conn The imquic_connection to send the request on * @param request_id The unique \c request_id value associated to the subscription to cancel_fetch from * @returns 0 in case of success, a negative integer otherwise */ int imquic_moq_cancel_fetch(imquic_connection *conn, uint64_t request_id); /*! \brief Function to send a \c TRACK_STATUS request - * \note Due to considerable changes between v12 and v13 on \c TRACK_STATUS , - * support for this request is disabled in versions earlier than v13. * @param conn The imquic_connection to send the request on * @param request_id A unique request ID to associate to this request * @param tns The imquic_moq_namespace namespace the track to track_status to belongs to @@ -1376,23 +1384,18 @@ int imquic_moq_cancel_fetch(imquic_connection *conn, uint64_t request_id); int imquic_moq_track_status(imquic_connection *conn, uint64_t request_id, imquic_moq_namespace *tns, imquic_moq_name *tn, imquic_moq_request_parameters *parameters); /*! \brief Function to accept an incoming \c TRACK_STATUS request - * \note Due to considerable changes between v12 and v13 on \c TRACK_STATUS , - * support for this request is disabled in versions earlier than v13. * @param conn The imquic_connection to send the request on * @param request_id The unique \c request_id value associated to the subscription to query - * @param track_alias The unique \c track_alias value associated to the subscription to query (ignored after v15) * @param parameters The parameters to add to the request * @returns 0 in case of success, a negative integer otherwise */ int imquic_moq_accept_track_status(imquic_connection *conn, uint64_t request_id, - uint64_t track_alias, imquic_moq_request_parameters *parameters); + imquic_moq_request_parameters *parameters); /*! \brief Function to reject an incoming \c TRACK_STATUS request - * @note Due to considerable changes between v12 and v13 on \c TRACK_STATUS , - * support for this request is disabled in versions earlier than v13 * @param conn The imquic_connection to send the request on * @param request_id The unique \c request_id value associated to the subscription to reject * @param error_code The error code to send back * @param reason A string representation of the error, if needed - * @param retry_interval Retry interval in ms (added in v16, ignored otherwise) + * @param retry_interval Retry interval in ms * @returns 0 in case of success, a negative integer otherwise */ int imquic_moq_reject_track_status(imquic_connection *conn, uint64_t request_id, imquic_moq_request_error_code error_code, const char *reason, uint64_t retry_interval); @@ -1414,8 +1417,9 @@ int imquic_moq_requests_blocked(imquic_connection *conn); /*! \brief Function to send a \c GOAWAY request * @param conn The imquic_connection to send the request on * @param uri Where the client can connect to continue the session + * @param timeout Timeout in ms to add to the request (added in v17, ignored for older versions) * @returns 0 in case of success, a negative integer otherwise */ -int imquic_moq_goaway(imquic_connection *conn, const char *uri); +int imquic_moq_goaway(imquic_connection *conn, const char *uri, uint64_t timeout); ///@} #endif diff --git a/src/internal/connection.h b/src/internal/connection.h index 039fa17..27b8332 100644 --- a/src/internal/connection.h +++ b/src/internal/connection.h @@ -146,6 +146,11 @@ void imquic_connection_notify_gone(imquic_connection *conn); * @param stream_id ID of the stream to reset * @param error_code The error code to add to the frame */ void imquic_connection_reset_stream(imquic_connection *conn, uint64_t stream_id, uint64_t error_code); +/*! \brief Helper to ask the peer to stop sending on a stream, sending a \c STOP_SENDING + * @param conn The imquic_connection instance that owns the stream to stop + * @param stream_id ID of the stream to stop + * @param error_code The error code to add to the frame */ +void imquic_connection_stop_sending_stream(imquic_connection *conn, uint64_t stream_id, uint64_t error_code); /*! \brief Helpers to close connections * @param conn The imquic_connection instance to close * @param error_code The error code to send back in the \c CONNECTION_CLOSE frame @@ -162,6 +167,7 @@ typedef enum imquic_connection_event_type { IMQUIC_CONNECTION_EVENT_STREAM, IMQUIC_CONNECTION_EVENT_DATAGRAM, IMQUIC_CONNECTION_EVENT_RESET_STREAM, + IMQUIC_CONNECTION_EVENT_STOP_SENDING, IMQUIC_CONNECTION_EVENT_CLOSE_CONN, } imquic_connection_event_type; diff --git a/src/internal/moq.h b/src/internal/moq.h index 9403510..563111c 100644 --- a/src/internal/moq.h +++ b/src/internal/moq.h @@ -4,7 +4,7 @@ * \brief Media Over QUIC (MoQ) stack (headers) * \details Implementation of the Media Over QUIC (MoQ) stack as part * of the library itself. At the time of writing, this implements (most - * of) versions from -06 to to -12 of the protocol. + * of) versions from -16 to to -17 of the protocol. * * \note This is the internal implementation of MoQ in the library. You're * still free to only use imquic as the underlying QUIC/WebTransport library, @@ -39,40 +39,32 @@ void imquic_moq_deinit(void); /*! \brief MoQ messages */ typedef enum imquic_moq_message_type { - IMQUIC_MOQ_REQUEST_OK = 0x5, /* Added in v15 */ - IMQUIC_MOQ_REQUEST_ERROR = 0x7, /* Added in v15 */ + IMQUIC_MOQ_REQUEST_OK = 0x5, + IMQUIC_MOQ_REQUEST_ERROR = 0x7, IMQUIC_MOQ_REQUEST_UPDATE = 0x2, IMQUIC_MOQ_SUBSCRIBE = 0x3, IMQUIC_MOQ_SUBSCRIBE_OK = 0x4, - IMQUIC_MOQ_SUBSCRIBE_ERROR = 0x5, /* Deprecated in v15 */ IMQUIC_MOQ_PUBLISH_NAMESPACE = 0x6, - IMQUIC_MOQ_PUBLISH_NAMESPACE_OK = 0x7, /* Deprecated in v15 */ - IMQUIC_MOQ_PUBLISH_NAMESPACE_ERROR = 0x8, /* Deprecated in v15 */ - IMQUIC_MOQ_PUBLISH_NAMESPACE_DONE = 0x9, - IMQUIC_MOQ_UNSUBSCRIBE = 0xa, + IMQUIC_MOQ_PUBLISH_NAMESPACE_DONE = 0x9, /* Deprecated in v17 */ + IMQUIC_MOQ_UNSUBSCRIBE = 0xa, /* Deprecated in v17 */ IMQUIC_MOQ_PUBLISH_DONE = 0xb, - IMQUIC_MOQ_PUBLISH_NAMESPACE_CANCEL = 0xc, + IMQUIC_MOQ_PUBLISH_NAMESPACE_CANCEL = 0xc, /* Deprecated in v17 */ IMQUIC_MOQ_TRACK_STATUS = 0xd, - IMQUIC_MOQ_TRACK_STATUS_OK = 0xe, /* Deprecated in v15 */ - IMQUIC_MOQ_TRACK_STATUS_ERROR = 0xf, /* Deprecated in v15 */ IMQUIC_MOQ_GOAWAY = 0x10, IMQUIC_MOQ_SUBSCRIBE_NAMESPACE = 0x11, - IMQUIC_MOQ_SUBSCRIBE_NAMESPACE_OK = 0x12, /* Deprecated in v15 */ - IMQUIC_MOQ_SUBSCRIBE_NAMESPACE_ERROR = 0x13, /* Deprecated in v15 */ - IMQUIC_MOQ_UNSUBSCRIBE_NAMESPACE = 0x14, /* Deprecated in v16 */ - IMQUIC_MOQ_NAMESPACE = 0x8, /* Added in v16 */ - IMQUIC_MOQ_NAMESPACE_DONE = 0xe, /* Added in v16 */ - IMQUIC_MOQ_MAX_REQUEST_ID = 0x15, - IMQUIC_MOQ_REQUESTS_BLOCKED = 0x1A, + IMQUIC_MOQ_NAMESPACE = 0x8, + IMQUIC_MOQ_NAMESPACE_DONE = 0xe, + IMQUIC_MOQ_PUBLISH_BLOCKED = 0xf, + IMQUIC_MOQ_MAX_REQUEST_ID = 0x15, /* Deprecated in v17 */ + IMQUIC_MOQ_REQUESTS_BLOCKED = 0x1A, /* Deprecated in v17 */ IMQUIC_MOQ_FETCH = 0x16, - IMQUIC_MOQ_FETCH_CANCEL = 0x17, + IMQUIC_MOQ_FETCH_CANCEL = 0x17, /* Deprecated in v17 */ IMQUIC_MOQ_FETCH_OK = 0x18, - IMQUIC_MOQ_FETCH_ERROR = 0x19, /* Deprecated in v15 */ - IMQUIC_MOQ_CLIENT_SETUP = 0x20, - IMQUIC_MOQ_SERVER_SETUP = 0x21, + IMQUIC_MOQ_SETUP = 0x2F00, + IMQUIC_MOQ_CLIENT_SETUP = 0x20, /* Deprecated in v17 */ + IMQUIC_MOQ_SERVER_SETUP = 0x21, /* Deprecated in v17 */ IMQUIC_MOQ_PUBLISH = 0x1D, IMQUIC_MOQ_PUBLISH_OK = 0x1E, - IMQUIC_MOQ_PUBLISH_ERROR = 0x1F, /* Deprecated in v15 */ } imquic_moq_message_type; /*! \brief Helper function to serialize to string the name of a imquic_moq_message_type value. * @param type The imquic_moq_message_type value @@ -96,25 +88,25 @@ gboolean imquic_moq_is_datagram_message_type_valid(imquic_moq_version version, u * for \c OBJECT_DATAGRAM or \c OBJECT_DATAGRAM_STATUS out of the individual properties. * @param version The version of the connection * @param payload Whether there is a payload - * @param ext Whether there are extensions + * @param prop Whether there are properties * @param eog Whether there is an End of Group - * @param oid Whether there is an Object ID (ignored before v14) - * @param priority Whether there is a Publisher Priority (ignored before v15) + * @param oid Whether there is an Object ID + * @param priority Whether there is a Publisher Priority * @returns The type as a bitmask flag */ uint8_t imquic_moq_datagram_message_type_return(imquic_moq_version version, - gboolean payload, gboolean ext, gboolean eog, gboolean oid, gboolean priority); + gboolean payload, gboolean prop, gboolean eog, gboolean oid, gboolean priority); /*! \brief Helper function to parse a imquic_moq_datagram_message_type value * for \c OBJECT_DATAGRAM or \c OBJECT_DATAGRAM_STATUS to the individual properties. * @param[in] version The version of the connection * @param[in] type The type to parse * @param[out] payload Output variable to write whether there is a payload - * @param[out] ext Output variable to write whether there are extensions + * @param[out] prop Output variable to write whether there are properties * @param[out] eog Output variable to write whether there is an End of Group - * @param[out] oid Output variable to write whether there is an Object ID (ignored before v14) - * @param[out] priority Output variable to write whether there is a Publisher Priority (added in v15) + * @param[out] oid Output variable to write whether there is an Object ID + * @param[out] priority Output variable to write whether there is a Publisher Priority * @param[out] violation Whether the type has bits set that really shouldn't */ void imquic_moq_datagram_message_type_parse(imquic_moq_version version, uint8_t type, - gboolean *payload, gboolean *ext, gboolean *eog, gboolean *oid, gboolean *priority, gboolean *violation); + gboolean *payload, gboolean *prop, gboolean *eog, gboolean *oid, gboolean *priority, gboolean *violation); /*! \brief Helper function to serialize to string the name of a imquic_moq_datagram_message_type value. * @param type The type value * @param version The version of the connection @@ -124,19 +116,11 @@ const char *imquic_moq_datagram_message_type_str(uint8_t type, imquic_moq_versio /*! \brief MoQ \c STREAM data messages */ typedef enum imquic_moq_data_message_type { /* IMQUIC_MOQ_SUBGROUP_HEADER_XXX */ - /* v11 */ - IMQUIC_MOQ_SUBGROUP_HEADER_NOSGID0_NOEXT_v11 = 0x8, /* Deprecated in v12 */ - IMQUIC_MOQ_SUBGROUP_HEADER_NOSGID0_v11 = 0x9, /* Deprecated in v12 */ - IMQUIC_MOQ_SUBGROUP_HEADER_NOSGID_NOEXT_v11 = 0xA, /* Deprecated in v12 */ - IMQUIC_MOQ_SUBGROUP_HEADER_NOSGID_v11 = 0xB, /* Deprecated in v12 */ - IMQUIC_MOQ_SUBGROUP_HEADER_NOEXT_v11 = 0xC, /* Deprecated in v12 */ - IMQUIC_MOQ_SUBGROUP_HEADER_v11 = 0xD, /* Deprecated in v12 */ - /* v12 and beyond */ - IMQUIC_MOQ_SUBGROUP_HEADER = 0x10, - IMQUIC_MOQ_SUBGROUP_HEADER_RANGE1_MIN = 0x10, - IMQUIC_MOQ_SUBGROUP_HEADER_RANGE1_MAX = 0x1D, - IMQUIC_MOQ_SUBGROUP_HEADER_RANGE2_MIN = 0x30, - IMQUIC_MOQ_SUBGROUP_HEADER_RANGE2_MAX = 0x3D, + IMQUIC_MOQ_SUBGROUP_HEADER = 0x10, + IMQUIC_MOQ_SUBGROUP_HEADER_RANGE1_MIN = 0x10, + IMQUIC_MOQ_SUBGROUP_HEADER_RANGE1_MAX = 0x1D, + IMQUIC_MOQ_SUBGROUP_HEADER_RANGE2_MIN = 0x30, + IMQUIC_MOQ_SUBGROUP_HEADER_RANGE2_MAX = 0x3D, /* IMQUIC_MOQ_FETCH_HEADER */ IMQUIC_MOQ_FETCH_HEADER = 0x5, } imquic_moq_data_message_type; @@ -149,23 +133,23 @@ gboolean imquic_moq_is_data_message_type_valid(imquic_moq_version version, uint8 * @param[in] version The version of the connection * @param[in] subgroup Whether the Subgroup ID field is present * @param[in] sgid0 Whether the default value of Subgroup ID is 0, in case the field is missing - * @param[in] ext Whether there are extensions + * @param[in] prop Whether there are properties * @param[in] eog Whether there is an End of Group - * @param[in] priority Whether there is a Publisher Priority (added in v15) + * @param[in] priority Whether there is a Publisher Priority * @returns The type as a bitmask flag */ uint8_t imquic_moq_data_message_type_from_subgroup_header(imquic_moq_version version, - gboolean subgroup, gboolean sgid0, gboolean ext, gboolean eog, gboolean priority); + gboolean subgroup, gboolean sgid0, gboolean prop, gboolean eog, gboolean priority); /*! \brief Helper function to parse a type value for \c SUBRGOUP_HEADER to the individual properties. * @param[in] version The version of the connection * @param[in] type The type to parse * @param[out] subgroup Output variable to write whether the Subgroup ID field is present * @param[out] sgid0 Output variable to write whether the default value of Subgroup ID is 0, in case the field is missing - * @param[out] ext Output variable to write whether there are extensions + * @param[out] prop Output variable to write whether there are properties * @param[out] eog Output variable to write whether there is an End of Group - * @param[out] priority Output variable to write whether there is a Publisher Priority (added in v15) + * @param[out] priority Output variable to write whether there is a Publisher Priority * @param[out] violation Whether the type has bits set that really shouldn't */ void imquic_moq_data_message_type_to_subgroup_header(imquic_moq_version version, uint8_t type, - gboolean *subgroup, gboolean *sgid0, gboolean *ext, gboolean *eog, gboolean *priority, gboolean *violation); + gboolean *subgroup, gboolean *sgid0, gboolean *prop, gboolean *eog, gboolean *priority, gboolean *violation); /*! \brief Helper function to serialize to string the name of a imquic_moq_data_message_type value. * @param type The imquic_data_moq_message_type value * @param version The version of the connection @@ -196,13 +180,13 @@ gboolean imquic_moq_is_fetch_serialization_flags_valid(imquic_moq_version versio * @param[in] oid Whether the Object ID field is present * @param[in] group Whether the Group ID field is present * @param[in] priority Whether the Publisher field is present - * @param[in] ext Whether there are extensions + * @param[in] prop Whether there are properties * @param[in] datagram Whether the forwarding preference is Datagram * @param[in] end_ne_range Whether this is the end of a non-existent range (ignores all other properties) * @param[in] end_uk_range Whether this is the end of an unknown range (ignores all other properties) * @returns The serialization flags as an integer */ uint64_t imquic_moq_generate_fetch_serialization_flags(imquic_moq_version version, - imquic_moq_fetch_subgroup_type subgroup, gboolean oid, gboolean group, gboolean priority, gboolean ext, + imquic_moq_fetch_subgroup_type subgroup, gboolean oid, gboolean group, gboolean priority, gboolean prop, gboolean datagram, gboolean end_ne_range, gboolean end_uk_range); /*! \brief Helper function to parse serialozation flags for \c FETCH to the individual properties. * @param[in] version The version of the connection @@ -210,44 +194,41 @@ uint64_t imquic_moq_generate_fetch_serialization_flags(imquic_moq_version versio * @param[out] subgroup Output variable to write the type of subgroup * @param[out] oid Output variable to write whether the Object ID field is present * @param[out] group Output variable to write whether the Group ID field is present - * @param[out] priority Output variable to write whether there is a Publisher Priority (added in v15) - * @param[out] ext Output variable to write whether there are extensions + * @param[out] priority Output variable to write whether there is a Publisher Priority + * @param[out] prop Output variable to write whether there are properties * @param[out] datagram Output variable to write whether the forwarding preference is Datagram * @param[out] end_ne_range Output variable to write whether this is the end of a non-existent range * @param[out] end_uk_range Output variable to write whether this is the end of an unknown range * @param[out] violation Whether the type has bits set that really shouldn't */ void imquic_moq_parse_fetch_serialization_flags(imquic_moq_version version, uint64_t flags, - imquic_moq_fetch_subgroup_type *subgroup, gboolean *oid, gboolean *group, gboolean *priority, gboolean *ext, + imquic_moq_fetch_subgroup_type *subgroup, gboolean *oid, gboolean *group, gboolean *priority, gboolean *prop, gboolean *datagram, gboolean *end_ne_range, gboolean *end_uk_range, gboolean *violation); -/*! \brief MoQ setup parameter types */ -typedef enum imquic_moq_setup_parameter_type { - IMQUIC_MOQ_SETUP_PARAM_PATH = 0x01, - IMQUIC_MOQ_SETUP_PARAM_MAX_REQUEST_ID = 0x02, - IMQUIC_MOQ_SETUP_PARAM_AUTHORIZATION_TOKEN = 0x03, - IMQUIC_MOQ_SETUP_PARAM_MAX_AUTH_TOKEN_CACHE_SIZE = 0x04, - IMQUIC_MOQ_SETUP_PARAM_AUTHORITY = 0x05, - IMQUIC_MOQ_SETUP_PARAM_MOQT_IMPLEMENTATION = 0x07, -} imquic_moq_setup_parameter_type; -/*! \brief Helper function to serialize to string the name of a imquic_moq_setup_parameter_type value. - * @param type The imquic_moq_setup_parameter_type value +/*! \brief MoQ setup option type */ +typedef enum imquic_moq_setup_option_type { + IMQUIC_MOQ_SETUP_OPTION_PATH = 0x01, + IMQUIC_MOQ_SETUP_OPTION_MAX_REQUEST_ID = 0x02, + IMQUIC_MOQ_SETUP_OPTION_AUTHORIZATION_TOKEN = 0x03, + IMQUIC_MOQ_SETUP_OPTION_MAX_AUTH_TOKEN_CACHE_SIZE = 0x04, + IMQUIC_MOQ_SETUP_OPTION_AUTHORITY = 0x05, + IMQUIC_MOQ_SETUP_OPTION_MOQT_IMPLEMENTATION = 0x07, +} imquic_moq_setup_option_type; +/*! \brief Helper function to serialize to string the name of a imquic_moq_setup_option_type value. + * @param type The imquic_moq_setup_option_type value * @returns The type name as a string, if valid, or NULL otherwise */ -const char *imquic_moq_setup_parameter_type_str(imquic_moq_setup_parameter_type type); +const char *imquic_moq_setup_option_type_str(imquic_moq_setup_option_type type); /*! \brief MoQ request parameter types */ typedef enum imquic_moq_request_parameter_type { - IMQUIC_MOQ_REQUEST_PARAM_AUTHORIZATION_TOKEN_v11 = 0x01, /* Deprecated in v12 */ IMQUIC_MOQ_REQUEST_PARAM_AUTHORIZATION_TOKEN = 0x03, IMQUIC_MOQ_REQUEST_PARAM_DELIVERY_TIMEOUT = 0x02, - IMQUIC_MOQ_REQUEST_PARAM_MAX_CACHE_DURATION = 0x04, /* Deprecated in v16 */ - IMQUIC_MOQ_REQUEST_PARAM_PUBLISHER_PRIORITY = 0x0E, /* Deprecated in v16 */ + IMQUIC_MOQ_REQUEST_PARAM_RENDEZVOUS_TIMEOUT = 0x04, IMQUIC_MOQ_REQUEST_PARAM_SUBSCRIBER_PRIORITY = 0x20, IMQUIC_MOQ_REQUEST_PARAM_GROUP_ORDER = 0x22, IMQUIC_MOQ_REQUEST_PARAM_SUBSCRIPTION_FILTER = 0x21, IMQUIC_MOQ_REQUEST_PARAM_EXPIRES = 0x8, IMQUIC_MOQ_REQUEST_PARAM_LARGEST_OBJECT = 0x9, IMQUIC_MOQ_REQUEST_PARAM_FORWARD = 0x10, - IMQUIC_MOQ_REQUEST_PARAM_DYNAMIC_GROUPS = 0x30, /* Deprecated in v16 */ IMQUIC_MOQ_REQUEST_PARAM_NEW_GROUP_REQUEST = 0x32, } imquic_moq_request_parameter_type; /*! \brief Helper function to serialize to string the name of a imquic_moq_request_parameter_type value. @@ -257,14 +238,16 @@ typedef enum imquic_moq_request_parameter_type { const char *imquic_moq_request_parameter_type_str(imquic_moq_request_parameter_type type, imquic_moq_version version); /*! \brief MoQ setup parameters */ -typedef struct imquic_moq_setup_parameters { +typedef struct imquic_moq_setup_options { /*! \brief Whether the PATH parameter is set */ gboolean path_set; /*! \brief Value of the PATH parameter */ char path[256]; - /*! \brief Whether the MAX_REQUEST_ID parameter is set */ + /*! \brief Whether the MAX_REQUEST_ID parameter is set + * \note Deprecated in v17 */ gboolean max_request_id_set; - /*! \brief Value of the MAX_REQUEST_ID parameter */ + /*! \brief Value of the MAX_REQUEST_ID parameter + * \note Deprecated in v17 */ uint64_t max_request_id; /*! \brief Whether the MAX_AUTH_TOKEN_CACHE_SIZE parameter is set */ gboolean max_auth_token_cache_size_set; @@ -286,22 +269,22 @@ typedef struct imquic_moq_setup_parameters { char moqt_implementation[256]; /*! \brief Whether there's unknown parameters */ gboolean unknown; -} imquic_moq_setup_parameters; +} imquic_moq_setup_options; -/*! \brief Helper mode to parse an extensions buffer to a GList of imquic_moq_object_extension +/*! \brief Helper mode to parse a properties buffer to a GList of imquic_moq_property * \note The caller owns the list, and is responsible of freeing it and its content * @param version The version of the connection - * @param extensions The buffer containing the extensions data - * @param elen The size of the buffer containing the extensions data - * @returns A GList instance containing a set of imquic_moq_object_extension, if successful, or NULL if no extensions were found */ -GList *imquic_moq_parse_object_extensions(imquic_moq_version version, uint8_t *extensions, size_t elen); -/*! \brief Helper mode to craft an extensions buffer out of a GList of imquic_moq_object_extension + * @param properties The buffer containing the properties data + * @param plen The size of the buffer containing the properties data + * @returns A GList instance containing a set of imquic_moq_property, if successful, or NULL if no properties were found */ +GList *imquic_moq_parse_properties(imquic_moq_version version, uint8_t *properties, size_t plen); +/*! \brief Helper mode to craft a properties buffer out of a GList of imquic_moq_property * @param[in] version The version of the connection - * @param[in] extensions The list of extensions to serialize - * @param[out] bytes The buffer to write the extensions data to + * @param[in] properties The list of properties to serialize + * @param[out] bytes The buffer to write the properties data to * @param[in] blen The size of the buffer to write to * @returns How many bytes were written, if successful */ -size_t imquic_moq_build_object_extensions(imquic_moq_version version, GList *extensions, uint8_t *bytes, size_t blen); +size_t imquic_moq_build_properties(imquic_moq_version version, GList *properties, uint8_t *bytes, size_t blen); /*! \brief MoQ FETCH types */ typedef enum imquic_moq_fetch_type { @@ -314,34 +297,6 @@ typedef enum imquic_moq_fetch_type { * @returns The type name as a string, if valid, or NULL otherwise */ const char *imquic_moq_fetch_type_str(imquic_moq_fetch_type type); -/*! \brief MoQ legacy (pre-v15) error codes and translation to/from new ones */ -typedef enum imquic_moq_legacy_error_code { - IMQUIC_MOQ_OLDERR_INTERNAL_ERROR = 0x0, - IMQUIC_MOQ_OLDERR_UNAUTHORIZED = 0x1, - IMQUIC_MOQ_OLDERR_TIMEOUT = 0x2, - IMQUIC_MOQ_OLDERR_NOT_SUPPORTED = 0x3, - IMQUIC_MOQ_OLDERR_UNINTERESTED = 0x4, - IMQUIC_MOQ_OLDERR_TRACK_DOES_NOT_EXIST = 0x4, - IMQUIC_MOQ_OLDERR_INVALID_RANGE = 0x5, - IMQUIC_MOQ_OLDERR_INVALID_JOINING_REQUEST_ID = 0x7, - IMQUIC_MOQ_OLDERR_UNKNOWN_STATUS_IN_RANGE = 0x8, - IMQUIC_MOQ_OLDERR_MALFORMED_TRACK = 0x9, - IMQUIC_MOQ_OLDERR_MALFORMED_AUTH_TOKEN = 0x10, - IMQUIC_MOQ_OLDERR_EXPIRED_AUTH_TOKEN = 0x12, -} imquic_moq_legacy_error_code; -/*! \brief Helper to translate a new request error code passed by the - * user to a legacy error code, if serializing on an old version of MoQ - * @param version The version this connection is using - * @param code The new request error code - * @returns The associated legacy error code, if available, or a generic internal error otherwise */ -imquic_moq_legacy_error_code imquic_moq_request_error_code_to_legacy(imquic_moq_version version, imquic_moq_request_error_code code); -/*! \brief Helper to translate a legacy error code parsed by the stack - * to a new request error code, if receiving from an old version of MoQ - * @param version The version this connection is using - * @param code The legacy error code - * @returns The associated new request error code, if available, or a generic internal error otherwise */ -imquic_moq_request_error_code imquic_moq_request_error_code_from_legacy(imquic_moq_version version, imquic_moq_legacy_error_code code); - /*! \brief MoQ context */ typedef struct imquic_moq_context { /*! \brief Associated QUIC connection */ @@ -362,18 +317,22 @@ typedef struct imquic_moq_context { gboolean is_server; /*! \brief Whether a MoQ control stream has been established */ gboolean has_control_stream; - /*! \brief ID of the control stream */ - uint64_t control_stream_id; + /*! \brief ID(s) of the control stream (legacy bidirectional, or new unidirectional) */ + uint64_t control_stream_id, remote_control_stream_id; + /*! \brief Whether we sent and received a SETUP */ + gboolean sent_setup, recvd_setup; /*! \brief QUIC streams handled by the stack */ GHashTable *streams; + /*! \brief Map of request streams, indexed by Request ID */ + GHashTable *streams_by_reqid; /*! \brief Subscriptions this connection will send objects to, indexed by track_alias */ GHashTable *subscriptions; /*! \brief Subscriptions this connection will send objects to, indexed by request_id */ GHashTable *subscriptions_by_id; - /*! \brief Track namespace subscriptions (served or asked), indexed by request_id */ - GHashTable *tns_subscriptions_by_id; /*! \brief Map of Request IDs and what they were for */ GHashTable *requests; + /*! \brief Map of Request IDs to Existing Request IDs, for updates */ + GHashTable *update_requests; /*! \brief Current Request IDs we expect and we can send */ uint64_t expected_request_id, next_request_id; /*! \brief Maximum Request IDs we can send and the one we accept */ @@ -386,6 +345,8 @@ typedef struct imquic_moq_context { imquic_mutex mutex; /*! \brief Whether we have established a connection */ volatile gint connected; + /*! \brief Check if it's time to handle pending streams */ + volatile gint check_pending; /*! \brief Whether we have received a GOAWAY */ volatile gint got_goaway; /*! \brief Whether this instance has been destroyed (reference counting) */ @@ -394,24 +355,41 @@ typedef struct imquic_moq_context { imquic_refcount ref; } imquic_moq_context; +/*! \brief MoQ stream request state + * \note Only needed for tracking the state of bidirectional requests, not media */ +typedef enum imquic_media_stream_request_state { + IMQUIC_MOQ_REQUEST_STATE_NEW = 0, + IMQUIC_MOQ_REQUEST_STATE_SENT, + IMQUIC_MOQ_REQUEST_STATE_OK, + IMQUIC_MOQ_REQUEST_STATE_ERROR, + IMQUIC_MOQ_REQUEST_STATE_UPDATE_SENT, + IMQUIC_MOQ_REQUEST_STATE_DONE, +} imquic_media_stream_request_state; +/*! \brief Helper function to serialize to string the name of a imquic_media_stream_request_state value. + * @param state The imquic_media_stream_request_state value + * @returns The state name as a string, if valid, or NULL otherwise */ +const char *imquic_media_stream_request_state_str(imquic_media_stream_request_state state); + /*! \brief MoQ stream * \note This is usually used for objects (e.g., via SUBGROUP_HEADER or - * FETCH), but starting in v16, namespace advertising uses streams too */ + * FETCH), but starting in v16, requests can use streams too */ typedef struct imquic_moq_stream { /*! \brief QUIC stream ID */ uint64_t stream_id; - /* Whether this STREAM is used for objects, or namespaces */ - gboolean subscribe_namespace, namespace_publisher; - /* State of SUBSCRIBE_NAMESPACE */ - volatile gint subscribe_namespace_state; + /*! In case this is a bidirectional STREAM for a request, the associated request type */ + imquic_moq_message_type request_type; + /*! In case this is a bidirectional STREAM for a request, if this endpoint originated it */ + gboolean request_sender; + /*! In case this is a bidirectional STREAM for a request, its current state */ + imquic_media_stream_request_state request_state; /*! \brief In case this is for SUBSCRIBE_NAMESPACE, the namespace prefix */ imquic_moq_namespace *namespace_prefix, *last_tuple; /*! \brief In case this is for SUBSCRIBE_NAMESPACE, how many tuples are in the namespace prefix */ uint8_t namespace_prefix_size; /*! \brief Delivery mode for this stream, in case it's used for objects */ imquic_moq_data_message_type type; - /*! \brief ID of the subscription */ - uint64_t request_id; + /*! \brief ID of the request/subscription, and of the update if one was involved */ + uint64_t request_id, update_request_id; /*! \brief Track alias */ uint64_t track_alias; /*! \brief Group ID */ @@ -438,7 +416,14 @@ typedef struct imquic_moq_stream { uint8_t last_priority; /*! \brief Whether we closed this stream */ gboolean closed; + /*! \brief Whether this instance has been destroyed (reference counting) */ + volatile gint destroyed; + /*! \brief Reference counter */ + imquic_refcount ref; } imquic_moq_stream; +/*! \brief Create a new MoQ stream + * @returns A pointer to a new moq_stream instance, if successful, or NULL otherwise */ +imquic_moq_stream *imquic_moq_stream_create(void); /*! \brief Destroy an existing MoQ stream * @param moq_stream MoQ stream to destroy */ void imquic_moq_stream_destroy(imquic_moq_stream *moq_stream); @@ -484,6 +469,7 @@ void imquic_moq_subscription_destroy(imquic_moq_subscription *moq_sub); * @returns 0 in case of success, or a negative integer otherwise */ int imquic_moq_parse_message(imquic_moq_context *moq, uint64_t stream_id, uint8_t *bytes, size_t blen, gboolean complete, gboolean datagram); /*! \brief Helper to parse a \c CLIENT_SETUP message + * \note This message was deprecated in v17: now both client and server use \c SETUP * @param[in] moq The imquic_moq_context instance the message is for * @param[in] bytes The buffer containing the message to parse * @param[in] blen Size of the buffer to parse @@ -491,13 +477,22 @@ int imquic_moq_parse_message(imquic_moq_context *moq, uint64_t stream_id, uint8_ * @returns The size of the parsed message, if successful, or 0 otherwise */ size_t imquic_moq_parse_client_setup(imquic_moq_context *moq, uint8_t *bytes, size_t blen, uint8_t *error); /*! \brief Helper to parse a \c SERVER_SETUP message + * \note This message was deprecated in v17: now both client and server use \c SETUP * @param[in] moq The imquic_moq_context instance the message is for * @param[in] bytes The buffer containing the message to parse * @param[in] blen Size of the buffer to parse * @param[out] error In/out property, initialized to 0 and set to something else in case of parsing errors * @returns The size of the parsed message, if successful, or 0 otherwise */ size_t imquic_moq_parse_server_setup(imquic_moq_context *moq, uint8_t *bytes, size_t blen, uint8_t *error); +/*! \brief Helper to parse a \c SETUP message + * @param[in] moq The imquic_moq_context instance the message is for + * @param[in] bytes The buffer containing the message to parse + * @param[in] blen Size of the buffer to parse + * @param[out] error In/out property, initialized to 0 and set to something else in case of parsing errors + * @returns The size of the parsed message, if successful, or 0 otherwise */ +size_t imquic_moq_parse_setup(imquic_moq_context *moq, uint8_t *bytes, size_t blen, uint8_t *error); /*! \brief Helper to parse a \c MAX_REQUEST_ID message + * \note This message was deprecated in v17 * @param[in] moq The imquic_moq_context instance the message is for * @param[in] bytes The buffer containing the message to parse * @param[in] blen Size of the buffer to parse @@ -505,6 +500,7 @@ size_t imquic_moq_parse_server_setup(imquic_moq_context *moq, uint8_t *bytes, si * @returns The size of the parsed message, if successful, or 0 otherwise */ size_t imquic_moq_parse_max_request_id(imquic_moq_context *moq, uint8_t *bytes, size_t blen, uint8_t *error); /*! \brief Helper to parse a \c REQUESTS_BLOCKED message + * \note This message was deprecated in v17 * @param[in] moq The imquic_moq_context instance the message is for * @param[in] bytes The buffer containing the message to parse * @param[in] blen Size of the buffer to parse @@ -512,22 +508,16 @@ size_t imquic_moq_parse_max_request_id(imquic_moq_context *moq, uint8_t *bytes, * @returns The size of the parsed message, if successful, or 0 otherwise */ size_t imquic_moq_parse_requests_blocked(imquic_moq_context *moq, uint8_t *bytes, size_t blen, uint8_t *error); /*! \brief Helper to parse a \c REQUEST_OK message - * \note This message was only added in v15, and consolidates most OK - * messages that existed until then. It will automatically trigger the - * right callback, by checking what the original request was for * @param[in] moq The imquic_moq_context instance the message is for - * @param[in] moq_stream The imquic_moq_stream instance the message came from (ignored before v16, only needed for namespaces) + * @param[in] moq_stream The imquic_moq_stream instance the message came from * @param[in] bytes The buffer containing the message to parse * @param[in] blen Size of the buffer to parse * @param[out] error In/out property, initialized to 0 and set to something else in case of parsing errors * @returns The size of the parsed message, if successful, or 0 otherwise */ size_t imquic_moq_parse_request_ok(imquic_moq_context *moq, imquic_moq_stream *moq_stream, uint8_t *bytes, size_t blen, uint8_t *error); /*! \brief Helper to parse a \c REQUEST_ERROR message - * \note This message was only added in v15, and consolidates all error - * messages that existed until then. It will automatically trigger the - * right callback, by checking what the original request was for * @param[in] moq The imquic_moq_context instance the message is for - * @param[in] moq_stream The imquic_moq_stream instance the message came from (ignored before v16, only needed for namespaces) + * @param[in] moq_stream The imquic_moq_stream instance the message came from * @param[in] bytes The buffer containing the message to parse * @param[in] blen Size of the buffer to parse * @param[out] error In/out property, initialized to 0 and set to something else in case of parsing errors @@ -535,26 +525,14 @@ size_t imquic_moq_parse_request_ok(imquic_moq_context *moq, imquic_moq_stream *m size_t imquic_moq_parse_request_error(imquic_moq_context *moq, imquic_moq_stream *moq_stream, uint8_t *bytes, size_t blen, uint8_t *error); /*! \brief Helper to parse a \c PUBLISH_NAMESPACE message * @param[in] moq The imquic_moq_context instance the message is for + * @param[in] moq_stream The imquic_moq_stream instance the message came from * @param[in] bytes The buffer containing the message to parse * @param[in] blen Size of the buffer to parse * @param[out] error In/out property, initialized to 0 and set to something else in case of parsing errors * @returns The size of the parsed message, if successful, or 0 otherwise */ -size_t imquic_moq_parse_publish_namespace(imquic_moq_context *moq, uint8_t *bytes, size_t blen, uint8_t *error); -/*! \brief Helper to parse a \c PUBLISH_NAMESPACE_OK message - * @param[in] moq The imquic_moq_context instance the message is for - * @param[in] bytes The buffer containing the message to parse - * @param[in] blen Size of the buffer to parse - * @param[out] error In/out property, initialized to 0 and set to something else in case of parsing errors - * @returns The size of the parsed message, if successful, or 0 otherwise */ -size_t imquic_moq_parse_publish_namespace_ok(imquic_moq_context *moq, uint8_t *bytes, size_t blen, uint8_t *error); -/*! \brief Helper to parse a \c PUBLISH_NAMESPACE_ERROR message - * @param[in] moq The imquic_moq_context instance the message is for - * @param[in] bytes The buffer containing the message to parse - * @param[in] blen Size of the buffer to parse - * @param[out] error In/out property, initialized to 0 and set to something else in case of parsing errors - * @returns The size of the parsed message, if successful, or 0 otherwise */ -size_t imquic_moq_parse_publish_namespace_error(imquic_moq_context *moq, uint8_t *bytes, size_t blen, uint8_t *error); +size_t imquic_moq_parse_publish_namespace(imquic_moq_context *moq, imquic_moq_stream *moq_stream, uint8_t *bytes, size_t blen, uint8_t *error); /*! \brief Helper to parse a \c PUBLISH_NAMESPACE_DONE message + * \note This message was deprecated in v17 * @param[in] moq The imquic_moq_context instance the message is for * @param[in] bytes The buffer containing the message to parse * @param[in] blen Size of the buffer to parse @@ -562,6 +540,7 @@ size_t imquic_moq_parse_publish_namespace_error(imquic_moq_context *moq, uint8_t * @returns The size of the parsed message, if successful, or 0 otherwise */ size_t imquic_moq_parse_publish_namespace_done(imquic_moq_context *moq, uint8_t *bytes, size_t blen, uint8_t *error); /*! \brief Helper to parse a \c PUBLISH_NAMESPACE_CANCEL message + * \note This message was deprecated in v17 * @param[in] moq The imquic_moq_context instance the message is for * @param[in] bytes The buffer containing the message to parse * @param[in] blen Size of the buffer to parse @@ -570,54 +549,46 @@ size_t imquic_moq_parse_publish_namespace_done(imquic_moq_context *moq, uint8_t size_t imquic_moq_parse_publish_namespace_cancel(imquic_moq_context *moq, uint8_t *bytes, size_t blen, uint8_t *error); /*! \brief Helper to parse a \c PUBLISH message * @param[in] moq The imquic_moq_context instance the message is for + * @param[in] moq_stream The imquic_moq_stream instance the message came from * @param[in] bytes The buffer containing the message to parse * @param[in] blen Size of the buffer to parse * @param[out] error In/out property, initialized to 0 and set to something else in case of parsing errors * @returns The size of the parsed message, if successful, or 0 otherwise */ -size_t imquic_moq_parse_publish(imquic_moq_context *moq, uint8_t *bytes, size_t blen, uint8_t *error); +size_t imquic_moq_parse_publish(imquic_moq_context *moq, imquic_moq_stream *moq_stream, uint8_t *bytes, size_t blen, uint8_t *error); /*! \brief Helper to parse a \c PUBLISH_OK message * @param[in] moq The imquic_moq_context instance the message is for + * @param[in] moq_stream The imquic_moq_stream instance the message came from * @param[in] bytes The buffer containing the message to parse * @param[in] blen Size of the buffer to parse * @param[out] error In/out property, initialized to 0 and set to something else in case of parsing errors * @returns The size of the parsed message, if successful, or 0 otherwise */ -size_t imquic_moq_parse_publish_ok(imquic_moq_context *moq, uint8_t *bytes, size_t blen, uint8_t *error); -/*! \brief Helper to parse a \c PUBLISH_ERROR message - * @param[in] moq The imquic_moq_context instance the message is for - * @param[in] bytes The buffer containing the message to parse - * @param[in] blen Size of the buffer to parse - * @param[out] error In/out property, initialized to 0 and set to something else in case of parsing errors - * @returns The size of the parsed message, if successful, or 0 otherwise */ -size_t imquic_moq_parse_publish_error(imquic_moq_context *moq, uint8_t *bytes, size_t blen, uint8_t *error); +size_t imquic_moq_parse_publish_ok(imquic_moq_context *moq, imquic_moq_stream *moq_stream, uint8_t *bytes, size_t blen, uint8_t *error); /*! \brief Helper to parse a \c SUBSCRIBE message * @param[in] moq The imquic_moq_context instance the message is for + * @param[in] moq_stream The imquic_moq_stream instance the message came from * @param[in] bytes The buffer containing the message to parse * @param[in] blen Size of the buffer to parse * @param[out] error In/out property, initialized to 0 and set to something else in case of parsing errors * @returns The size of the parsed message, if successful, or 0 otherwise */ -size_t imquic_moq_parse_subscribe(imquic_moq_context *moq, uint8_t *bytes, size_t blen, uint8_t *error); +size_t imquic_moq_parse_subscribe(imquic_moq_context *moq, imquic_moq_stream *moq_stream, uint8_t *bytes, size_t blen, uint8_t *error); /*! \brief Helper to parse a \c REQUEST_UPDATE message * @param[in] moq The imquic_moq_context instance the message is for + * @param[in] moq_stream The imquic_moq_stream instance the message came from * @param[in] bytes The buffer containing the message to parse * @param[in] blen Size of the buffer to parse * @param[out] error In/out property, initialized to 0 and set to something else in case of parsing errors * @returns The size of the parsed message, if successful, or 0 otherwise */ -size_t imquic_moq_parse_request_update(imquic_moq_context *moq, uint8_t *bytes, size_t blen, uint8_t *error); +size_t imquic_moq_parse_request_update(imquic_moq_context *moq, imquic_moq_stream *moq_stream, uint8_t *bytes, size_t blen, uint8_t *error); /*! \brief Helper to parse a \c SUBSCRIBE_OK message * @param[in] moq The imquic_moq_context instance the message is for + * @param[in] moq_stream The imquic_moq_stream instance the message came from * @param[in] bytes The buffer containing the message to parse * @param[in] blen Size of the buffer to parse * @param[out] error In/out property, initialized to 0 and set to something else in case of parsing errors * @returns The size of the parsed message, if successful, or 0 otherwise */ -size_t imquic_moq_parse_subscribe_ok(imquic_moq_context *moq, uint8_t *bytes, size_t blen, uint8_t *error); -/*! \brief Helper to parse a \c SUBSCRIBE_ERROR message - * @param[in] moq The imquic_moq_context instance the message is for - * @param[in] bytes The buffer containing the message to parse - * @param[in] blen Size of the buffer to parse - * @param[out] error In/out property, initialized to 0 and set to something else in case of parsing errors - * @returns The size of the parsed message, if successful, or 0 otherwise */ -size_t imquic_moq_parse_subscribe_error(imquic_moq_context *moq, uint8_t *bytes, size_t blen, uint8_t *error); +size_t imquic_moq_parse_subscribe_ok(imquic_moq_context *moq, imquic_moq_stream *moq_stream, uint8_t *bytes, size_t blen, uint8_t *error); /*! \brief Helper to parse an \c UNSUBSCRIBE message + * \note This message was deprecated in v17 * @param[in] moq The imquic_moq_context instance the message is for * @param[in] bytes The buffer containing the message to parse * @param[in] blen Size of the buffer to parse @@ -626,42 +597,21 @@ size_t imquic_moq_parse_subscribe_error(imquic_moq_context *moq, uint8_t *bytes, size_t imquic_moq_parse_unsubscribe(imquic_moq_context *moq, uint8_t *bytes, size_t blen, uint8_t *error); /*! \brief Helper to parse a \c PUBLISH_DONE message * @param[in] moq The imquic_moq_context instance the message is for + * @param[in] moq_stream The imquic_moq_stream instance the message came from * @param[in] bytes The buffer containing the message to parse * @param[in] blen Size of the buffer to parse * @param[out] error In/out property, initialized to 0 and set to something else in case of parsing errors * @returns The size of the parsed message, if successful, or 0 otherwise */ -size_t imquic_moq_parse_publish_done(imquic_moq_context *moq, uint8_t *bytes, size_t blen, uint8_t *error); +size_t imquic_moq_parse_publish_done(imquic_moq_context *moq, imquic_moq_stream *moq_stream, uint8_t *bytes, size_t blen, uint8_t *error); /*! \brief Helper to parse a \c SUBSCRIBE_NAMESPACE message * @param[in] moq The imquic_moq_context instance the message is for - * @param[in] moq_stream The imquic_moq_stream instance the message came from (ignored before v16) + * @param[in] moq_stream The imquic_moq_stream instance the message came from * @param[in] bytes The buffer containing the message to parse * @param[in] blen Size of the buffer to parse * @param[out] error In/out property, initialized to 0 and set to something else in case of parsing errors * @returns The size of the parsed message, if successful, or 0 otherwise */ size_t imquic_moq_parse_subscribe_namespace(imquic_moq_context *moq, imquic_moq_stream *moq_stream, uint8_t *bytes, size_t blen, uint8_t *error); -/*! \brief Helper to parse a \c SUBSCRIBE_NAMESPACE_OK message - * @param[in] moq The imquic_moq_context instance the message is for - * @param[in] bytes The buffer containing the message to parse - * @param[in] blen Size of the buffer to parse - * @param[out] error In/out property, initialized to 0 and set to something else in case of parsing errors - * @returns The size of the parsed message, if successful, or 0 otherwise */ -size_t imquic_moq_parse_subscribe_namespace_ok(imquic_moq_context *moq, uint8_t *bytes, size_t blen, uint8_t *error); -/*! \brief Helper to parse a \c SUBSCRIBE_NAMESPACE_ERROR message - * @param[in] moq The imquic_moq_context instance the message is for - * @param[in] bytes The buffer containing the message to parse - * @param[in] blen Size of the buffer to parse - * @param[out] error In/out property, initialized to 0 and set to something else in case of parsing errors - * @returns The size of the parsed message, if successful, or 0 otherwise */ -size_t imquic_moq_parse_subscribe_namespace_error(imquic_moq_context *moq, uint8_t *bytes, size_t blen, uint8_t *error); -/*! \brief Helper to parse an \c UNSUBSCRIBE_NAMESPACE message - * @param[in] moq The imquic_moq_context instance the message is for - * @param[in] bytes The buffer containing the message to parse - * @param[in] blen Size of the buffer to parse - * @param[out] error In/out property, initialized to 0 and set to something else in case of parsing errors - * @returns The size of the parsed message, if successful, or 0 otherwise */ -size_t imquic_moq_parse_unsubscribe_namespace(imquic_moq_context *moq, uint8_t *bytes, size_t blen, uint8_t *error); /*! \brief Helper to parse a \c NAMESPACE message - * \note This method was added in v16, to notify namespaces on a dedicated STREAM * @param[in] moq The imquic_moq_context instance the message is for * @param[in] moq_stream The imquic_moq_stream instance the message came from * @param[in] bytes The buffer containing the message to parse @@ -670,7 +620,6 @@ size_t imquic_moq_parse_unsubscribe_namespace(imquic_moq_context *moq, uint8_t * * @returns The size of the parsed message, if successful, or 0 otherwise */ size_t imquic_moq_parse_namespace(imquic_moq_context *moq, imquic_moq_stream *moq_stream, uint8_t *bytes, size_t blen, uint8_t *error); /*! \brief Helper to parse a \c NAMESPACE_DONE message - * \note This method was added in v16, to notify namespaces on a dedicated STREAM * @param[in] moq The imquic_moq_context instance the message is for * @param[in] moq_stream The imquic_moq_stream instance the message came from * @param[in] bytes The buffer containing the message to parse @@ -678,55 +627,46 @@ size_t imquic_moq_parse_namespace(imquic_moq_context *moq, imquic_moq_stream *mo * @param[out] error In/out property, initialized to 0 and set to something else in case of parsing errors * @returns The size of the parsed message, if successful, or 0 otherwise */ size_t imquic_moq_parse_namespace_done(imquic_moq_context *moq, imquic_moq_stream *moq_stream, uint8_t *bytes, size_t blen, uint8_t *error); -/*! \brief Helper to parse a \c FETCH message +/*! \brief Helper to parse a \c PUBLISH_BLOCKED message * @param[in] moq The imquic_moq_context instance the message is for + * @param[in] moq_stream The imquic_moq_stream instance the message came from * @param[in] bytes The buffer containing the message to parse * @param[in] blen Size of the buffer to parse * @param[out] error In/out property, initialized to 0 and set to something else in case of parsing errors * @returns The size of the parsed message, if successful, or 0 otherwise */ -size_t imquic_moq_parse_fetch(imquic_moq_context *moq, uint8_t *bytes, size_t blen, uint8_t *error); -/*! \brief Helper to parse a \c FETCH_CANCEL message +size_t imquic_moq_parse_publish_blocked(imquic_moq_context *moq, imquic_moq_stream *moq_stream, uint8_t *bytes, size_t blen, uint8_t *error); +/*! \brief Helper to parse a \c FETCH message * @param[in] moq The imquic_moq_context instance the message is for + * @param[in] moq_stream The imquic_moq_stream instance the message came from * @param[in] bytes The buffer containing the message to parse * @param[in] blen Size of the buffer to parse * @param[out] error In/out property, initialized to 0 and set to something else in case of parsing errors * @returns The size of the parsed message, if successful, or 0 otherwise */ -size_t imquic_moq_parse_fetch_cancel(imquic_moq_context *moq, uint8_t *bytes, size_t blen, uint8_t *error); -/*! \brief Helper to parse a \c FETCH_OK message +size_t imquic_moq_parse_fetch(imquic_moq_context *moq, imquic_moq_stream *moq_stream, uint8_t *bytes, size_t blen, uint8_t *error); +/*! \brief Helper to parse a \c FETCH_CANCEL message + * \note This message was deprecated in v17 * @param[in] moq The imquic_moq_context instance the message is for * @param[in] bytes The buffer containing the message to parse * @param[in] blen Size of the buffer to parse * @param[out] error In/out property, initialized to 0 and set to something else in case of parsing errors * @returns The size of the parsed message, if successful, or 0 otherwise */ -size_t imquic_moq_parse_fetch_ok(imquic_moq_context *moq, uint8_t *bytes, size_t blen, uint8_t *error); -/*! \brief Helper to parse a \c FETCH_ERROR message +size_t imquic_moq_parse_fetch_cancel(imquic_moq_context *moq, uint8_t *bytes, size_t blen, uint8_t *error); +/*! \brief Helper to parse a \c FETCH_OK message * @param[in] moq The imquic_moq_context instance the message is for + * @param[in] moq_stream The imquic_moq_stream instance the message came from * @param[in] bytes The buffer containing the message to parse * @param[in] blen Size of the buffer to parse * @param[out] error In/out property, initialized to 0 and set to something else in case of parsing errors * @returns The size of the parsed message, if successful, or 0 otherwise */ -size_t imquic_moq_parse_fetch_error(imquic_moq_context *moq, uint8_t *bytes, size_t blen, uint8_t *error); +size_t imquic_moq_parse_fetch_ok(imquic_moq_context *moq, imquic_moq_stream *moq_stream, uint8_t *bytes, size_t blen, uint8_t *error); /*! \brief Helper to parse a \c TRACK_STATUS message * @param[in] moq The imquic_moq_context instance the message is for + * @param[in] moq_stream The imquic_moq_stream instance the message came from * @param[in] bytes The buffer containing the message to parse * @param[in] blen Size of the buffer to parse * @param[out] error In/out property, initialized to 0 and set to something else in case of parsing errors * @returns The size of the parsed message, if successful, or 0 otherwise */ -size_t imquic_moq_parse_track_status(imquic_moq_context *moq, uint8_t *bytes, size_t blen, uint8_t *error); -/*! \brief Helper to parse a \c TRACK_STATUS_OK message - * @param[in] moq The imquic_moq_context instance the message is for - * @param[in] bytes The buffer containing the message to parse - * @param[in] blen Size of the buffer to parse - * @param[out] error In/out property, initialized to 0 and set to something else in case of parsing errors - * @returns The size of the parsed message, if successful, or 0 otherwise */ -size_t imquic_moq_parse_track_status_ok(imquic_moq_context *moq, uint8_t *bytes, size_t blen, uint8_t *error); -/*! \brief Helper to parse a \c TRACK_STATUS_ERROR message - * @param[in] moq The imquic_moq_context instance the message is for - * @param[in] bytes The buffer containing the message to parse - * @param[in] blen Size of the buffer to parse - * @param[out] error In/out property, initialized to 0 and set to something else in case of parsing errors - * @returns The size of the parsed message, if successful, or 0 otherwise */ -size_t imquic_moq_parse_track_status_error(imquic_moq_context *moq, uint8_t *bytes, size_t blen, uint8_t *error); +size_t imquic_moq_parse_track_status(imquic_moq_context *moq, imquic_moq_stream *moq_stream, uint8_t *bytes, size_t blen, uint8_t *error); /*! \brief Helper to parse an \c OBJECT_DATAGRAM message * @param[in] moq The imquic_moq_context instance the message is for * @param[in] bytes The buffer containing the message to parse @@ -791,24 +731,33 @@ size_t imquic_moq_parse_goaway(imquic_moq_context *moq, uint8_t *bytes, size_t b */ ///@{ /*! \brief Helper method to add a \c CLIENT_SETUP message to a buffer + * \note This message was deprecated in v17: now both client and server use \c SETUP * @param moq The imquic_moq_context generating the message * @param bytes The buffer to add the message to * @param blen The size of the buffer - * @param supported_versions List of supported versions - * @param parameters The setup parameters to send + * @param options The setup options to send * @returns The size of the generated message, if successful, or 0 otherwise */ size_t imquic_moq_add_client_setup(imquic_moq_context *moq, uint8_t *bytes, size_t blen, - GList *supported_versions, imquic_moq_setup_parameters *parameters); + imquic_moq_setup_options *options); /*! \brief Helper method to add a \c SERVER_SETUP message to a buffer + * \note This message was deprecated in v17: now both client and server use \c SETUP * @param moq The imquic_moq_context generating the message * @param bytes The buffer to add the message to * @param blen The size of the buffer - * @param version Negotiated version - * @param parameters The setup parameters to send + * @param options The setup options to send * @returns The size of the generated message, if successful, or 0 otherwise */ size_t imquic_moq_add_server_setup(imquic_moq_context *moq, uint8_t *bytes, size_t blen, - uint32_t version, imquic_moq_setup_parameters *parameters); + imquic_moq_setup_options *options); +/*! \brief Helper method to add a \c SETUP message to a buffer + * @param moq The imquic_moq_context generating the message + * @param bytes The buffer to add the message to + * @param blen The size of the buffer + * @param options The setup options to send + * @returns The size of the generated message, if successful, or 0 otherwise */ +size_t imquic_moq_add_setup(imquic_moq_context *moq, uint8_t *bytes, size_t blen, + imquic_moq_setup_options *options); /*! \brief Helper method to add a \c MAX_REQUEST_ID message to a buffer + * \note This message was deprecated in v17 * @param moq The imquic_moq_context generating the message * @param bytes The buffer to add the message to * @param blen The size of the buffer @@ -816,6 +765,7 @@ size_t imquic_moq_add_server_setup(imquic_moq_context *moq, uint8_t *bytes, size * @returns The size of the generated message, if successful, or 0 otherwise */ size_t imquic_moq_add_max_request_id(imquic_moq_context *moq, uint8_t *bytes, size_t blen, uint64_t max_request_id); /*! \brief Helper method to add a \c REQUESTS_BLOCKED message to a buffer + * \note This message was deprecated in v17 * @param moq The imquic_moq_context generating the message * @param bytes The buffer to add the message to * @param blen The size of the buffer @@ -823,8 +773,6 @@ size_t imquic_moq_add_max_request_id(imquic_moq_context *moq, uint8_t *bytes, si * @returns The size of the generated message, if successful, or 0 otherwise */ size_t imquic_moq_add_requests_blocked(imquic_moq_context *moq, uint8_t *bytes, size_t blen, uint64_t max_request_id); /*! \brief Helper method to add a \c REQUEST_OK message to a buffer - * \note This message was only added in v15, and consolidates most OK - * messages that existed until then * @param moq The imquic_moq_context generating the message * @param moq_stream The imquic_moq_stream instance the message is for * @param bytes The buffer to add the message to @@ -835,8 +783,6 @@ size_t imquic_moq_add_requests_blocked(imquic_moq_context *moq, uint8_t *bytes, size_t imquic_moq_add_request_ok(imquic_moq_context *moq, imquic_moq_stream *moq_stream, uint8_t *bytes, size_t blen, uint64_t request_id, imquic_moq_request_parameters *parameters); /*! \brief Helper method to add a \c REQUEST_ERROR message to a buffer - * \note This message was only added in v15, and consolidates all - * error messages that existed until then * @param moq The imquic_moq_context generating the message * @param moq_stream The imquic_moq_stream instance the message is for * @param bytes The buffer to add the message to @@ -844,28 +790,33 @@ size_t imquic_moq_add_request_ok(imquic_moq_context *moq, imquic_moq_stream *moq * @param request_id The request ID to put in the message * @param error Error code associated to the message * @param reason Verbose description of the error, if any - * @param retry_interval Retry interval in ms (added in v16, ignored otherwise) + * @param retry_interval Retry interval in ms * @returns The size of the generated message, if successful, or 0 otherwise */ size_t imquic_moq_add_request_error(imquic_moq_context *moq, imquic_moq_stream *moq_stream, uint8_t *bytes, size_t blen, uint64_t request_id, uint64_t error, const char *reason, uint64_t retry_interval); /*! \brief Helper method to add a \c PUBLISH_NAMESPACE message to a buffer * @param moq The imquic_moq_context generating the message + * @param moq_stream The imquic_moq_stream instance the message is for * @param bytes The buffer to add the message to * @param blen The size of the buffer * @param request_id The request ID to put in the message + * @param required_id_delta The required request ID delta to put in the message (ignored before v17) * @param track_namespace Namespace to publish_namespace * @param parameters The parameters to add, if any * @returns The size of the generated message, if successful, or 0 otherwise */ -size_t imquic_moq_add_publish_namespace(imquic_moq_context *moq, uint8_t *bytes, size_t blen, - uint64_t request_id, imquic_moq_namespace *track_namespace, imquic_moq_request_parameters *parameters); -/*! \brief Helper method to add a \c PUBLISH_NAMESPACE_OK message to a buffer +size_t imquic_moq_add_publish_namespace(imquic_moq_context *moq, imquic_moq_stream *moq_stream, + uint8_t *bytes, size_t blen, uint64_t request_id, uint64_t required_id_delta, + imquic_moq_namespace *track_namespace, imquic_moq_request_parameters *parameters); +/*! \brief Helper method to add a \c PUBLISH_NAMESPACE_DONE message to a buffer + * \note This message was deprecated in v17 * @param moq The imquic_moq_context generating the message * @param bytes The buffer to add the message to * @param blen The size of the buffer * @param request_id The request ID to put in the message * @returns The size of the generated message, if successful, or 0 otherwise */ -size_t imquic_moq_add_publish_namespace_ok(imquic_moq_context *moq, uint8_t *bytes, size_t blen, uint64_t request_id); -/*! \brief Helper method to add a \c PUBLISH_NAMESPACE_ERROR message to a buffer +size_t imquic_moq_add_publish_namespace_done(imquic_moq_context *moq, uint8_t *bytes, size_t blen, uint64_t request_id); +/*! \brief Helper method to add a \c PUBLISH_NAMESPACE_CANCEL message to a buffer + * \note This message was deprecated in v17 * @param moq The imquic_moq_context generating the message * @param bytes The buffer to add the message to * @param blen The size of the buffer @@ -873,103 +824,76 @@ size_t imquic_moq_add_publish_namespace_ok(imquic_moq_context *moq, uint8_t *byt * @param error Error code associated to the message * @param reason Verbose description of the error, if any * @returns The size of the generated message, if successful, or 0 otherwise */ -size_t imquic_moq_add_publish_namespace_error(imquic_moq_context *moq, uint8_t *bytes, size_t blen, - uint64_t request_id, imquic_moq_request_error_code error, const char *reason); -/*! \brief Helper method to add a \c PUBLISH_NAMESPACE_DONE message to a buffer - * @param moq The imquic_moq_context generating the message - * @param bytes The buffer to add the message to - * @param blen The size of the buffer - * @param track_namespace Namespace to publish_namespace_done - * @returns The size of the generated message, if successful, or 0 otherwise */ -size_t imquic_moq_add_publish_namespace_done(imquic_moq_context *moq, uint8_t *bytes, size_t blen, imquic_moq_namespace *track_namespace); -/*! \brief Helper method to add aN \c PUBLISH_NAMESPACE_CANCEL message to a buffer - * @param moq The imquic_moq_context generating the message - * @param bytes The buffer to add the message to - * @param blen The size of the buffer - * @param track_namespace Namespace for which to cancel the publish_namespacement - * @param error Error code associated to the message - * @param reason Verbose description of the error, if any - * @returns The size of the generated message, if successful, or 0 otherwise */ -size_t imquic_moq_add_publish_namespace_cancel(imquic_moq_context *moq, uint8_t *bytes, size_t blen, imquic_moq_namespace *track_namespace, +size_t imquic_moq_add_publish_namespace_cancel(imquic_moq_context *moq, uint8_t *bytes, size_t blen, uint64_t request_id, imquic_moq_request_error_code error, const char *reason); /*! \brief Helper to add a \c PUBLISH message to a buffer * @param moq The imquic_moq_context generating the message + * @param moq_stream The imquic_moq_stream instance the message is for * @param bytes The buffer to add the message to * @param blen The size of the buffer * @param request_id The request ID to put in the message + * @param required_id_delta The required request ID delta to put in the message (ignored before v17) * @param track_namespace The namespace to put in the message * @param track_name The track name to put in the message * @param track_alias The track alias to put in the message * @param parameters The parameters to add, if any - * @param track_extensions List of track extensions to add, if any (added in v16) + * @param track_properties List of track properties to add, if any * @returns The size of the generated message, if successful, or 0 otherwise */ -size_t imquic_moq_add_publish(imquic_moq_context *moq, uint8_t *bytes, size_t blen, uint64_t request_id, +size_t imquic_moq_add_publish(imquic_moq_context *moq, imquic_moq_stream *moq_stream, + uint8_t *bytes, size_t blen, uint64_t request_id, uint64_t required_id_delta, imquic_moq_namespace *track_namespace, imquic_moq_name *track_name, uint64_t track_alias, - imquic_moq_request_parameters *parameters, GList *track_extensions); + imquic_moq_request_parameters *parameters, GList *track_properties); /*! \brief Helper method to add a \c PUBLISH_OK message to a buffer * @param moq The imquic_moq_context generating the message + * @param moq_stream The imquic_moq_stream instance the message is for * @param bytes The buffer to add the message to * @param blen The size of the buffer * @param request_id The request ID to put in the message * @param parameters The parameters to add, if any * @returns The size of the generated message, if successful, or 0 otherwise */ -size_t imquic_moq_add_publish_ok(imquic_moq_context *moq, uint8_t *bytes, size_t blen, uint64_t request_id, - imquic_moq_request_parameters *parameters); -/*! \brief Helper method to add a \c PUBLISH_ERRROR message to a buffer - * @param moq The imquic_moq_context generating the message - * @param bytes The buffer to add the message to - * @param blen The size of the buffer - * @param request_id The request ID to put in the message - * @param error Error code associated to the message - * @param reason Verbose description of the error, if any - * @returns The size of the generated message, if successful, or 0 otherwise */ -size_t imquic_moq_add_publish_error(imquic_moq_context *moq, uint8_t *bytes, size_t blen, uint64_t request_id, - imquic_moq_request_error_code error, const char *reason); +size_t imquic_moq_add_publish_ok(imquic_moq_context *moq, imquic_moq_stream *moq_stream, + uint8_t *bytes, size_t blen, uint64_t request_id, imquic_moq_request_parameters *parameters); /*! \brief Helper to add a \c SUBSCRIBE message to a buffer * @param moq The imquic_moq_context generating the message + * @param moq_stream The imquic_moq_stream instance the message is for * @param bytes The buffer to add the message to * @param blen The size of the buffer * @param request_id The request ID to put in the message - * @param track_alias The track alias to put in the message + * @param required_id_delta The required request ID delta to put in the message (ignored before v17) * @param track_namespace The namespace to put in the message * @param track_name The track name to put in the message * @param parameters The parameters to add, if any * @returns The size of the generated message, if successful, or 0 otherwise */ -size_t imquic_moq_add_subscribe(imquic_moq_context *moq, uint8_t *bytes, size_t blen, uint64_t request_id, uint64_t track_alias, +size_t imquic_moq_add_subscribe(imquic_moq_context *moq, imquic_moq_stream *moq_stream, + uint8_t *bytes, size_t blen, uint64_t request_id, uint64_t required_id_delta, imquic_moq_namespace *track_namespace, imquic_moq_name *track_name, imquic_moq_request_parameters *parameters); /*! \brief Helper method to add a \c REQUEST_UPDATE message to a buffer * @param moq The imquic_moq_context generating the message + * @param moq_stream The imquic_moq_stream instance the message is for * @param bytes The buffer to add the message to * @param blen The size of the buffer * @param request_id The request ID to put in the message - * @param sub_request_id The subscription request ID to put in the message (ignored before v14) + * @param sub_request_id The subscription request ID to put in the message (not added to message starting in v17) + * @param required_id_delta The required request ID delta to put in the message (ignored before v17) * @param parameters The parameters to add, if any * @returns The size of the generated message, if successful, or 0 otherwise */ -size_t imquic_moq_add_request_update(imquic_moq_context *moq, uint8_t *bytes, size_t blen, - uint64_t request_id, uint64_t sub_request_id, imquic_moq_request_parameters *parameters); +size_t imquic_moq_add_request_update(imquic_moq_context *moq, imquic_moq_stream *moq_stream, + uint8_t *bytes, size_t blen, uint64_t request_id, uint64_t sub_request_id, uint64_t required_id_delta, imquic_moq_request_parameters *parameters); /*! \brief Helper method to add a \c SUBSCRIBE_OK message to a buffer * @param moq The imquic_moq_context generating the message + * @param moq_stream The imquic_moq_stream instance the message is for * @param bytes The buffer to add the message to * @param blen The size of the buffer * @param request_id The request ID to put in the message - * @param track_alias The track alias to put in the message (ignored before v12) + * @param track_alias The track alias to put in the message * @param parameters The parameters to add, if any - * @param track_extensions List of track extensions to add, if any (added in v16) - * @returns The size of the generated message, if successful, or 0 otherwise */ -size_t imquic_moq_add_subscribe_ok(imquic_moq_context *moq, uint8_t *bytes, size_t blen, uint64_t request_id, - uint64_t track_alias, imquic_moq_request_parameters *parameters, GList *track_extensions); -/*! \brief Helper method to add a \c SUBSCRIBE_ERRROR message to a buffer - * @param moq The imquic_moq_context generating the message - * @param bytes The buffer to add the message to - * @param blen The size of the buffer - * @param request_id The request ID to put in the message - * @param error Error code associated to the message - * @param reason Verbose description of the error, if any - * @param track_alias The track alias to put in the message (ignored starting from v12) + * @param track_properties List of track properties to add, if any * @returns The size of the generated message, if successful, or 0 otherwise */ -size_t imquic_moq_add_subscribe_error(imquic_moq_context *moq, uint8_t *bytes, size_t blen, uint64_t request_id, - imquic_moq_request_error_code error, const char *reason, uint64_t track_alias); +size_t imquic_moq_add_subscribe_ok(imquic_moq_context *moq, imquic_moq_stream *moq_stream, + uint8_t *bytes, size_t blen, uint64_t request_id, + uint64_t track_alias, imquic_moq_request_parameters *parameters, GList *track_properties); /*! \brief Helper method to add an \c UNSUBSCRIBE message to a buffer + * \note This message was deprecated in v17 * @param moq The imquic_moq_context generating the message * @param bytes The buffer to add the message to * @param blen The size of the buffer @@ -978,6 +902,7 @@ size_t imquic_moq_add_subscribe_error(imquic_moq_context *moq, uint8_t *bytes, s size_t imquic_moq_add_unsubscribe(imquic_moq_context *moq, uint8_t *bytes, size_t blen, uint64_t request_id); /*! \brief Helper method to add a \c PUBLISH_DONE message to a buffer * @param moq The imquic_moq_context generating the message + * @param moq_stream The imquic_moq_stream instance the message is for * @param bytes The buffer to add the message to * @param blen The size of the buffer * @param request_id The request ID to put in the message @@ -985,7 +910,8 @@ size_t imquic_moq_add_unsubscribe(imquic_moq_context *moq, uint8_t *bytes, size_ * @param streams_count The streams count * @param reason Verbose description of the status * @returns The size of the generated message, if successful, or 0 otherwise */ -size_t imquic_moq_add_publish_done(imquic_moq_context *moq, uint8_t *bytes, size_t blen, uint64_t request_id, +size_t imquic_moq_add_publish_done(imquic_moq_context *moq, imquic_moq_stream *moq_stream, + uint8_t *bytes, size_t blen, uint64_t request_id, imquic_moq_pub_done_code status, uint64_t streams_count, const char *reason); /*! \brief Helper to add a \c SUBSCRIBE_NAMESPACE message to a buffer * @param moq The imquic_moq_context generating the message @@ -993,37 +919,14 @@ size_t imquic_moq_add_publish_done(imquic_moq_context *moq, uint8_t *bytes, size * @param bytes The buffer to add the message to * @param blen The size of the buffer * @param request_id The request ID to put in the message + * @param required_id_delta The required request ID delta to put in the message (ignored before v17) * @param track_namespace The namespace to put in the message - * @param subscribe_options The subscribe options to put in the message (added in v16) + * @param subscribe_options The subscribe options to put in the message * @param parameters The parameters to add, if any * @returns The size of the generated message, if successful, or 0 otherwise */ -size_t imquic_moq_add_subscribe_namespace(imquic_moq_context *moq, imquic_moq_stream *moq_stream, uint8_t *bytes, size_t blen, - uint64_t request_id, imquic_moq_namespace *track_namespace, imquic_moq_subscribe_namespace_options subscribe_options, imquic_moq_request_parameters *parameters); -/*! \brief Helper method to add a \c SUBSCRIBE_NAMESPACE_OK message to a buffer - * @param moq The imquic_moq_context generating the message - * @param bytes The buffer to add the message to - * @param blen The size of the buffer - * @param request_id The request ID to put in the message - * @returns The size of the generated message, if successful, or 0 otherwise */ -size_t imquic_moq_add_subscribe_namespace_ok(imquic_moq_context *moq, uint8_t *bytes, size_t blen, uint64_t request_id); -/*! \brief Helper method to add a \c SUBSCRIBE_NAMESPACE_ERRROR message to a buffer - * @param moq The imquic_moq_context generating the message - * @param bytes The buffer to add the message to - * @param blen The size of the buffer - * @param request_id The request ID to put in the message - * @param error Error code associated to the message - * @param reason Verbose description of the error, if any - * @returns The size of the generated message, if successful, or 0 otherwise */ -size_t imquic_moq_add_subscribe_namespace_error(imquic_moq_context *moq, uint8_t *bytes, size_t blen, - uint64_t request_id, imquic_moq_request_error_code error, const char *reason); -/*! \brief Helper method to add an \c UNSUBSCRIBE_NAMESPACE message to a buffer - * @param moq The imquic_moq_context generating the message - * @param bytes The buffer to add the message to - * @param blen The size of the buffer - * @param request_id The request ID to put in the message (added in v15) - * @param track_namespace The namespace to put in the message (ignored starting from v15) - * @returns The size of the generated message, if successful, or 0 otherwise */ -size_t imquic_moq_add_unsubscribe_namespace(imquic_moq_context *moq, uint8_t *bytes, size_t blen, uint64_t request_id, imquic_moq_namespace *track_namespace); +size_t imquic_moq_add_subscribe_namespace(imquic_moq_context *moq, imquic_moq_stream *moq_stream, + uint8_t *bytes, size_t blen, uint64_t request_id, uint64_t required_id_delta, + imquic_moq_namespace *track_namespace, imquic_moq_subscribe_namespace_options subscribe_options, imquic_moq_request_parameters *parameters); /*! \brief Helper method to add a \c NAMESPACE_DONE message to a buffer * @param moq The imquic_moq_context generating the message * @param moq_stream The imquic_moq_stream instance the message is for @@ -1042,12 +945,24 @@ size_t imquic_moq_add_namespace(imquic_moq_context *moq, imquic_moq_stream *moq_ * @returns The size of the generated message, if successful, or 0 otherwise */ size_t imquic_moq_add_namespace_done(imquic_moq_context *moq, imquic_moq_stream *moq_stream, uint8_t *bytes, size_t blen, imquic_moq_namespace *track_namespace_suffix); +/*! \brief Helper method to add a \c PUBLISH_BLOCKED message to a buffer + * @param moq The imquic_moq_context generating the message + * @param moq_stream The imquic_moq_stream instance the message is for + * @param bytes The buffer to add the message to + * @param blen The size of the buffer + * @param track_namespace_suffix Namespace suffix that is impacted + * @param track Track that is blocked + * @returns The size of the generated message, if successful, or 0 otherwise */ +size_t imquic_moq_add_publish_blocked(imquic_moq_context *moq, imquic_moq_stream *moq_stream, + uint8_t *bytes, size_t blen, imquic_moq_namespace *track_namespace_suffix, imquic_moq_name *track); /*! \brief Helper to add a \c FETCH message to a buffer * @param moq The imquic_moq_context generating the message + * @param moq_stream The imquic_moq_stream instance the message is for * @param bytes The buffer to add the message to * @param blen The size of the buffer * @param type The FETCH type * @param request_id The request ID to put in the message + * @param required_id_delta The required request ID delta to put in the message (ignored before v17) * @param joining_request_id The joining request ID to put in the message, if any * @param preceding_group_offset The preceding group offset for joining fetches, if any * @param track_namespace The namespace to put in the message @@ -1055,11 +970,13 @@ size_t imquic_moq_add_namespace_done(imquic_moq_context *moq, imquic_moq_stream * @param range The Start/End Locations to put in the message * @param parameters The parameters to add, if any * @returns The size of the generated message, if successful, or 0 otherwise */ -size_t imquic_moq_add_fetch(imquic_moq_context *moq, uint8_t *bytes, size_t blen, imquic_moq_fetch_type type, - uint64_t request_id, uint64_t joining_request_id, uint64_t preceding_group_offset, +size_t imquic_moq_add_fetch(imquic_moq_context *moq, imquic_moq_stream *moq_stream, + uint8_t *bytes, size_t blen, imquic_moq_fetch_type type, + uint64_t request_id, uint64_t required_id_delta, uint64_t joining_request_id, uint64_t preceding_group_offset, imquic_moq_namespace *track_namespace, imquic_moq_name *track_name, imquic_moq_location_range *range, imquic_moq_request_parameters *parameters); /*! \brief Helper method to add an \c FETCH_CANCEL message to a buffer + * \note This message was deprecated in v17 * @param moq The imquic_moq_context generating the message * @param bytes The buffer to add the message to * @param blen The size of the buffer @@ -1068,28 +985,21 @@ size_t imquic_moq_add_fetch(imquic_moq_context *moq, uint8_t *bytes, size_t blen size_t imquic_moq_add_fetch_cancel(imquic_moq_context *moq, uint8_t *bytes, size_t blen, uint64_t request_id); /*! \brief Helper method to add a \c FETCH_OK message to a buffer * @param moq The imquic_moq_context generating the message + * @param moq_stream The imquic_moq_stream instance the message is for * @param bytes The buffer to add the message to * @param blen The size of the buffer * @param request_id The request ID to put in the message * @param end_of_track Whether all objects have been published * @param end_location End location to add to the message, if needed * @param parameters The parameters to add, if any - * @param track_extensions List of track extensions to add, if any (added in v16) - * @returns The size of the generated message, if successful, or 0 otherwise */ -size_t imquic_moq_add_fetch_ok(imquic_moq_context *moq, uint8_t *bytes, size_t blen, uint64_t request_id, - uint8_t end_of_track, imquic_moq_location *end_location, imquic_moq_request_parameters *parameters, GList *track_extensions); -/*! \brief Helper method to add a \c FETCH_ERRROR message to a buffer - * @param moq The imquic_moq_context generating the message - * @param bytes The buffer to add the message to - * @param blen The size of the buffer - * @param request_id The request ID to put in the message - * @param error Error code associated to the message - * @param reason Verbose description of the error, if any + * @param track_properties List of track properties to add, if any * @returns The size of the generated message, if successful, or 0 otherwise */ -size_t imquic_moq_add_fetch_error(imquic_moq_context *moq, uint8_t *bytes, size_t blen, uint64_t request_id, - imquic_moq_request_error_code error, const char *reason); +size_t imquic_moq_add_fetch_ok(imquic_moq_context *moq, imquic_moq_stream *moq_stream, + uint8_t *bytes, size_t blen, uint64_t request_id, + uint8_t end_of_track, imquic_moq_location *end_location, imquic_moq_request_parameters *parameters, GList *track_properties); /*! \brief Helper to add a \c TRACK_STATUS message to a buffer * @param moq The imquic_moq_context generating the message + * @param moq_stream The imquic_moq_stream instance the message is for * @param bytes The buffer to add the message to * @param blen The size of the buffer * @param request_id The request ID to put in the message @@ -1097,35 +1007,17 @@ size_t imquic_moq_add_fetch_error(imquic_moq_context *moq, uint8_t *bytes, size_ * @param track_name The track name to put in the message * @param parameters The parameters to add, if any * @returns The size of the generated message, if successful, or 0 otherwise */ -size_t imquic_moq_add_track_status(imquic_moq_context *moq, uint8_t *bytes, size_t blen, uint64_t request_id, +size_t imquic_moq_add_track_status(imquic_moq_context *moq, imquic_moq_stream *moq_stream, + uint8_t *bytes, size_t blen, uint64_t request_id, imquic_moq_namespace *track_namespace, imquic_moq_name *track_name, imquic_moq_request_parameters *parameters); -/*! \brief Helper method to add a \c TRACK_STATUS_OK message to a buffer - * @param moq The imquic_moq_context generating the message - * @param bytes The buffer to add the message to - * @param blen The size of the buffer - * @param request_id The request ID to put in the message - * @param track_alias The track alias to put in the message - * @param parameters The parameters to add, if any - * @returns The size of the generated message, if successful, or 0 otherwise */ -size_t imquic_moq_add_track_status_ok(imquic_moq_context *moq, uint8_t *bytes, size_t blen, uint64_t request_id, - uint64_t track_alias, imquic_moq_request_parameters *parameters); -/*! \brief Helper method to add a \c TRACK_STATUS_ERRROR message to a buffer - * @param moq The imquic_moq_context generating the message - * @param bytes The buffer to add the message to - * @param blen The size of the buffer - * @param request_id The request ID to put in the message - * @param error Error code associated to the message - * @param reason Verbose description of the error, if any - * @returns The size of the generated message, if successful, or 0 otherwise */ -size_t imquic_moq_add_track_status_error(imquic_moq_context *moq, uint8_t *bytes, size_t blen, uint64_t request_id, - imquic_moq_request_error_code error, const char *reason); /*! \brief Helper method to add a \c GOAWAY message to a buffer * @param moq The imquic_moq_context generating the message * @param bytes The buffer to add the message to * @param blen The size of the buffer * @param new_session_uri New uri value to put in the message, if any + * @param timeout Timeout to put in the message (added in v17, ignored for older versions) * @returns The size of the generated message, if successful, or 0 otherwise */ -size_t imquic_moq_add_goaway(imquic_moq_context *moq, uint8_t *bytes, size_t blen, const char *new_session_uri); +size_t imquic_moq_add_goaway(imquic_moq_context *moq, uint8_t *bytes, size_t blen, const char *new_session_uri, uint64_t timeout); /*! \brief Helper to add an \c OBJECT_DATAGRAM message to a buffer * @note This assumes the connection negotiated \c DATAGRAM support * @param moq The imquic_moq_context generating the message @@ -1139,12 +1031,12 @@ size_t imquic_moq_add_goaway(imquic_moq_context *moq, uint8_t *bytes, size_t ble * @param priority The publisher priority to put in the message * @param payload The buffer containing the payload of the object * @param plen The size of the payload buffer - * @param extensions The buffer containing the object extensions, if any - * @param elen The size of the object extensions buffer + * @param properties The buffer containing the properties, if any + * @param prlen The size of the properties buffer * @returns The size of the generated message, if successful, or 0 otherwise */ size_t imquic_moq_add_object_datagram(imquic_moq_context *moq, uint8_t *bytes, size_t blen, uint64_t request_id, uint64_t track_alias, uint64_t group_id, uint64_t object_id, uint64_t object_status, uint8_t priority, - uint8_t *payload, size_t plen, uint8_t *extensions, size_t elen); + uint8_t *payload, size_t plen, uint8_t *properties, size_t prlen); /*! \brief Helper to add an \c OBJECT_DATAGRAM_STATUS message to a buffer * @note This assumes the connection negotiated \c DATAGRAM support * @param moq The imquic_moq_context generating the message @@ -1155,12 +1047,12 @@ size_t imquic_moq_add_object_datagram(imquic_moq_context *moq, uint8_t *bytes, s * @param object_id The object ID to put in the message * @param priority The publisher priority to put in the message * @param object_status The object status (only added if the payload length is 0) - * @param extensions The buffer containing the object extensions, if any - * @param elen The size of the object extensions buffer + * @param properties The buffer containing the properties, if any + * @param prlen The size of the properties buffer * @returns The size of the generated message, if successful, or 0 otherwise */ size_t imquic_moq_add_object_datagram_status(imquic_moq_context *moq, uint8_t *bytes, size_t blen, uint64_t track_alias, uint64_t group_id, uint64_t object_id, uint8_t priority, - uint64_t object_status, uint8_t *extensions, size_t elen); + uint64_t object_status, uint8_t *properties, size_t prlen); /*! \brief Helper to add a \c SUBGROUP_HEADER message to a buffer * @note This will create a new \c STREAM and send the header: after * that, imquic_moq_add_stream_header_subgroup_object is used to send @@ -1187,12 +1079,12 @@ size_t imquic_moq_add_subgroup_header(imquic_moq_context *moq, imquic_moq_stream * @param object_status The object status (only added if the payload length is 0) * @param payload The buffer containing the payload of the object * @param plen The size of the payload buffer - * @param extensions The buffer containing the object extensions, if any - * @param elen The size of the object extensions buffer + * @param properties The buffer containing the properties, if any + * @param prlen The size of the properties buffer * @returns The size of the generated object, if successful, or 0 otherwise */ size_t imquic_moq_add_subgroup_header_object(imquic_moq_context *moq, imquic_moq_stream *moq_stream, uint8_t *bytes, size_t blen, uint64_t object_id, uint64_t object_status, - uint8_t *payload, size_t plen, uint8_t *extensions, size_t elen); + uint8_t *payload, size_t plen, uint8_t *properties, size_t prlen); /*! \brief Helper to add a \c FETCH_HEADER message to a buffer * @note This will create a new \c STREAM and send the header: after * that, imquic_moq_add_fetch_header_object is used to send @@ -1208,7 +1100,7 @@ size_t imquic_moq_add_fetch_header(imquic_moq_context *moq, uint8_t *bytes, size * @param moq The imquic_moq_context generating the object * @param bytes The buffer to add the object to * @param blen The size of the buffer - * @param flags The serialization flags (added in v15) + * @param flags The serialization flags * @param group_id The group ID * @param subgroup_id The subgroup ID * @param object_id The object ID @@ -1216,49 +1108,74 @@ size_t imquic_moq_add_fetch_header(imquic_moq_context *moq, uint8_t *bytes, size * @param object_status The object status (only added if the payload length is 0) * @param payload The buffer containing the payload of the object * @param plen The size of the payload buffer - * @param extensions The buffer containing the object extensions, if any - * @param elen The size of the object extensions buffer + * @param properties The buffer containing the properties, if any + * @param prlen The size of the properties buffer * @returns The size of the generated object, if successful, or 0 otherwise */ size_t imquic_moq_add_fetch_header_object(imquic_moq_context *moq, uint8_t *bytes, size_t blen, uint64_t flags, uint64_t group_id, uint64_t subgroup_id, uint64_t object_id, uint8_t priority, - uint64_t object_status, uint8_t *payload, size_t plen, uint8_t *extensions, size_t elen); -/*! \brief Helper method to add object extensions to a buffer + uint64_t object_status, uint8_t *payload, size_t plen, uint8_t *properties, size_t prlen); +/*! \brief Helper method to add properties to a buffer * @param moq The imquic_moq_context generating the message - * @param bytes The buffer to add the extensions to + * @param bytes The buffer to add the properties to * @param blen The size of the buffer - * @param extensions The buffer containing the object extensions, if any - * @param elen The size of the object extensions buffer - * @returns The size of the generated extensions block, if successful, or 0 otherwise */ -size_t imquic_moq_add_object_extensions(imquic_moq_context *moq, uint8_t *bytes, size_t blen, - uint8_t *extensions, size_t elen); + * @param properties The buffer containing the properties, if any + * @param prlen The size of the properties buffer + * @returns The size of the generated properties block, if successful, or 0 otherwise */ +size_t imquic_moq_add_properties(imquic_moq_context *moq, uint8_t *bytes, size_t blen, + uint8_t *properties, size_t prlen); ///@} -/** @name Parsing and building MoQ parameters +/** @name Parsing and building MoQ setup options (TLV) */ ///@{ +/*! \brief Helper to add a MoQ setup option with a numeric value to a buffer + * @param moq The imquic_moq_context instance the setup option is for + * @param bytes Buffer to add the setup option to + * @param blen Size of the buffer + * @param opt ID of the setup option to add + * @param prev ID of the previously added setup option, if we're delta-encoding + * @param number The numeric value of the setup option to add + * @returns The size of the setup option, if successful, or 0 otherwise */ +size_t imquic_moq_setup_option_add_int(imquic_moq_context *moq, uint8_t *bytes, size_t blen, + uint64_t opt, uint64_t prev, uint64_t number); +/*! \brief Helper to add a MoQ setup option with generic data to a buffer + * @param moq The imquic_moq_context instance the setup option is for + * @param bytes Buffer to add the setup option to + * @param blen Size of the buffer + * @param opt ID of the setup option to add + * @param prev ID of the previously added setup option, if we're delta-encoding + * @param buf The data acting as a value for the setup option to add + * @param buflen The size of the data value + * @returns The size of the setup option, if successful, or 0 otherwise */ +size_t imquic_moq_setup_option_add_data(imquic_moq_context *moq, uint8_t *bytes, size_t blen, + uint64_t opt, uint64_t prev, uint8_t *buf, size_t buflen); /*! \brief Helper method to parse a MoQ setup parameter * @note This method does nothing at the moment * @param[in] moq The imquic_moq_context instance to update with the new parameter * @param[in] bytes Buffer containing the parameter to parse * @param[in] blen Size of the buffer to parse - * @param[out] params imquic_moq_setup_parameters instance to put the parsed parameter in - * @param[out] param_type Type of the parsed parameter, needed for delta-decoding - * @param[out] error In/out property, initialized to 0 and set to something else in case of parsing errors - * @returns The size of the parameter, if successful, or 0 otherwise */ -size_t imquic_moq_parse_setup_parameter(imquic_moq_context *moq, uint8_t *bytes, size_t blen, - imquic_moq_setup_parameters *params, uint64_t *param_type, uint8_t *error); -/*! \brief Helper method to parse a MoQ subscribe parameter - * @note This method does nothing at the moment - * @param[in] moq The imquic_moq_context instance to update with the new parameter - * @param[in] bytes Buffer containing the parameter to parse - * @param[in] blen Size of the buffer to parse - * @param[out] params imquic_moq_request_parameters instance to put the parsed parameter in + * @param[out] params imquic_moq_setup_options instance to put the parsed parameter in * @param[out] param_type Type of the parsed parameter, needed for delta-decoding * @param[out] error In/out property, initialized to 0 and set to something else in case of parsing errors * @returns The size of the parameter, if successful, or 0 otherwise */ -size_t imquic_moq_parse_request_parameter(imquic_moq_context *moq, uint8_t *bytes, size_t blen, - imquic_moq_request_parameters *params, uint64_t *param_type, uint8_t *error); -/*! \brief Helper to add a MoQ (setup or subscribe) parameter with a numeric value to a buffer +size_t imquic_moq_parse_setup_option(imquic_moq_context *moq, uint8_t *bytes, size_t blen, + imquic_moq_setup_options *params, uint64_t *param_type, uint8_t *error); +/*! \brief Helper to serialize a imquic_moq_setup_options set to a buffer + * @param[in] moq The imquic_moq_context instance the parameter is for + * @param[in] options The imquic_moq_setup_options to serialize + * @param[out] bytes The buffer to add paramerers to + * @param[in] blen The size of the buffer + * @param[out] params_num The number of parameters added to the buffer + * @returns The size of the serialized parameters, if successful, or 0 otherwise */ +size_t imquic_moq_setup_options_serialize(imquic_moq_context *moq, + imquic_moq_setup_options *options, + uint8_t *bytes, size_t blen, uint8_t *params_num); +///@} + +/** @name Parsing and building MoQ parameters (TLV or not, depending on version) + */ +///@{ +/*! \brief Helper to add a MoQ parameter with a numeric value to a buffer as a varint * @param moq The imquic_moq_context instance the parameter is for * @param bytes Buffer to add the parameter to * @param blen Size of the buffer @@ -1266,9 +1183,29 @@ size_t imquic_moq_parse_request_parameter(imquic_moq_context *moq, uint8_t *byte * @param prev ID of the previously added parameter, if we're delta-encoding * @param number The numeric value of the parameter to add * @returns The size of the parameter, if successful, or 0 otherwise */ -size_t imquic_moq_parameter_add_int(imquic_moq_context *moq, uint8_t *bytes, size_t blen, +size_t imquic_moq_parameter_add_varint(imquic_moq_context *moq, uint8_t *bytes, size_t blen, uint64_t param, uint64_t prev, uint64_t number); -/*! \brief Helper to add a MoQ (setup or subscribe) parameter with generic data to a buffer +/*! \brief Helper to add a MoQ parameter with a numeric value to a buffer as a byte + * @param moq The imquic_moq_context instance the parameter is for + * @param bytes Buffer to add the parameter to + * @param blen Size of the buffer + * @param param ID of the parameter to add + * @param prev ID of the previously added parameter, if we're delta-encoding + * @param number The numeric value of the parameter to add + * @returns The size of the parameter, if successful, or 0 otherwise */ +size_t imquic_moq_parameter_add_uint8(imquic_moq_context *moq, uint8_t *bytes, size_t blen, + uint64_t param, uint64_t prev, uint8_t number); +/*! \brief Helper to add a MoQ parameter with a location to a buffer + * @param moq The imquic_moq_context instance the parameter is for + * @param bytes Buffer to add the parameter to + * @param blen Size of the buffer + * @param param ID of the parameter to add + * @param prev ID of the previously added parameter, if we're delta-encoding + * @param location The location parameter to add + * @returns The size of the parameter, if successful, or 0 otherwise */ +size_t imquic_moq_parameter_add_location(imquic_moq_context *moq, uint8_t *bytes, size_t blen, + uint64_t param, uint64_t prev, imquic_moq_location *location); +/*! \brief Helper to add a MoQ parameter with generic data to a buffer * @param moq The imquic_moq_context instance the parameter is for * @param bytes Buffer to add the parameter to * @param blen Size of the buffer @@ -1279,26 +1216,27 @@ size_t imquic_moq_parameter_add_int(imquic_moq_context *moq, uint8_t *bytes, siz * @returns The size of the parameter, if successful, or 0 otherwise */ size_t imquic_moq_parameter_add_data(imquic_moq_context *moq, uint8_t *bytes, size_t blen, uint64_t param, uint64_t prev, uint8_t *buf, size_t buflen); -/*! \brief Helper to serialize a imquic_moq_setup_parameters set to a buffer - * @param[in] moq The imquic_moq_context instance the parameter is for - * @param[in] parameters The imquic_moq_setup_parameters to serialize - * @param[out] bytes The buffer to add paramerers to - * @param[in] blen The size of the buffer - * @param[out] params_num The number of parameters added to the buffer - * @returns The size of the serialized parameters, if successful, or 0 otherwise */ -size_t imquic_moq_setup_parameters_serialize(imquic_moq_context *moq, - imquic_moq_setup_parameters *parameters, - uint8_t *bytes, size_t blen, uint8_t *params_num); - +/*! \brief Helper method to parse a MoQ subscribe parameter + * @note This method does nothing at the moment + * @param[in] moq The imquic_moq_context instance to update with the new parameter + * @param[in] bytes Buffer containing the parameter to parse + * @param[in] blen Size of the buffer to parse + * @param[out] params imquic_moq_request_parameters instance to put the parsed parameter in + * @param[out] param_type Type of the parsed parameter, needed for delta-decoding + * @param[out] error In/out property, initialized to 0 and set to something else in case of parsing errors + * @returns The size of the parameter, if successful, or 0 otherwise */ +size_t imquic_moq_parse_request_parameter(imquic_moq_context *moq, uint8_t *bytes, size_t blen, + imquic_moq_request_parameters *params, uint64_t *param_type, uint8_t *error); /*! \brief Helper to serialize a imquic_moq_request_parameters set to a buffer * @param[in] moq The imquic_moq_context instance the parameter is for + * @param[in] request The imquic_moq_message_type request originating the parameters to serialize * @param[in] parameters The imquic_moq_request_parameters to serialize * @param[out] bytes The buffer to add paramerers to * @param[in] blen The size of the buffer * @param[out] params_num The number of parameters added to the buffer * @returns The size of the serialized parameters, if successful, or 0 otherwise */ size_t imquic_moq_request_parameters_serialize(imquic_moq_context *moq, - imquic_moq_request_parameters *parameters, + imquic_moq_message_type request, imquic_moq_request_parameters *parameters, uint8_t *bytes, size_t blen, uint8_t *params_num); ///@} @@ -1314,76 +1252,80 @@ typedef struct imquic_moq_callbacks { /*! \brief Callback function to be notified when a MoQ connection is ready (setup performed on both ends) */ void (* moq_ready)(imquic_connection *conn); /*! \brief Callback function to be notified about incoming \c PUBLISH_NAMESPACE messages */ - void (* incoming_publish_namespace)(imquic_connection *conn, uint64_t request_id, imquic_moq_namespace *tns, imquic_moq_request_parameters *parameters); - /*! \brief Callback function to be notified about incoming \c PUBLISH_NAMESPACE_CANCEL messages */ - void (* incoming_publish_namespace_cancel)(imquic_connection *conn, imquic_moq_namespace *tns, imquic_moq_request_error_code error_code, const char *reason); + void (* incoming_publish_namespace)(imquic_connection *conn, uint64_t request_id, uint64_t required_id_delta, imquic_moq_namespace *tns, imquic_moq_request_parameters *parameters); + /*! \brief Callback function to be notified about incoming \c PUBLISH_NAMESPACE_CANCEL messages, or when the bidirectional stream is closed */ + void (* incoming_publish_namespace_cancel)(imquic_connection *conn, uint64_t request_id, imquic_moq_request_error_code error_code, const char *reason); /*! \brief Callback function to be notified about incoming \c PUBLISH_NAMESPACE_ACCEPTED messages */ void (* publish_namespace_accepted)(imquic_connection *conn, uint64_t request_id, imquic_moq_request_parameters *parameters); /*! \brief Callback function to be notified about incoming \c PUBLISH_NAMESPACE_ERROR messages */ void (* publish_namespace_error)(imquic_connection *conn, uint64_t request_id, imquic_moq_request_error_code error_code, const char *reason, uint64_t retry_interval); - /*! \brief Callback function to be notified about incoming \c PUBLISH_NAMESPACE_DONE messages */ - void (* publish_namespace_done)(imquic_connection *conn, imquic_moq_namespace *tns); + /*! \brief Callback function to be notified about incoming \c PUBLISH_NAMESPACE_DONE messages + * \note This message was deprecated in v17 */ + void (* publish_namespace_done)(imquic_connection *conn, uint64_t request_id); /*! \brief Callback function to be notified about incoming \c PUBLISH messages */ - void (* incoming_publish)(imquic_connection *conn, uint64_t request_id, imquic_moq_namespace *tns, imquic_moq_name *tn, - uint64_t track_alias, imquic_moq_request_parameters *parameters, GList *track_extensions); + void (* incoming_publish)(imquic_connection *conn, uint64_t request_id, uint64_t required_id_delta, imquic_moq_namespace *tns, imquic_moq_name *tn, + uint64_t track_alias, imquic_moq_request_parameters *parameters, GList *track_properties); /*! \brief Callback function to be notified about incoming \c PUBLISH_ACCEPTED messages */ void (* publish_accepted)(imquic_connection *conn, uint64_t request_id, imquic_moq_request_parameters *parameters); /*! \brief Callback function to be notified about incoming \c PUBLISH_ERROR messages */ void (* publish_error)(imquic_connection *conn, uint64_t request_id, imquic_moq_request_error_code error_code, const char *reason, uint64_t retry_interval); /*! \brief Callback function to be notified about incoming \c SUBSCRIBE messages */ - void (* incoming_subscribe)(imquic_connection *conn, uint64_t request_id, uint64_t track_alias, + void (* incoming_subscribe)(imquic_connection *conn, uint64_t request_id, uint64_t required_id_delta, imquic_moq_namespace *tns, imquic_moq_name *tn, imquic_moq_request_parameters *parameters); /*! \brief Callback function to be notified about incoming \c SUBSCRIBE_ACCEPTED messages */ - void (* subscribe_accepted)(imquic_connection *conn, uint64_t request_id, uint64_t track_alias, imquic_moq_request_parameters *parameters, GList *track_extensions); + void (* subscribe_accepted)(imquic_connection *conn, uint64_t request_id, uint64_t track_alias, imquic_moq_request_parameters *parameters, GList *track_properties); /*! \brief Callback function to be notified about incoming \c SUBSCRIBE_ERROR messages */ - void (* subscribe_error)(imquic_connection *conn, uint64_t request_id, imquic_moq_request_error_code error_code, const char *reason, uint64_t track_alias, uint64_t retry_interval); + void (* subscribe_error)(imquic_connection *conn, uint64_t request_id, imquic_moq_request_error_code error_code, const char *reason, uint64_t retry_interval); /*! \brief Callback function to be notified about incoming \c REQUEST_UPDATE messages */ - void (* request_updated)(imquic_connection *conn, uint64_t request_id, uint64_t sub_request_id, imquic_moq_request_parameters *parameters); + void (* request_updated)(imquic_connection *conn, uint64_t request_id, uint64_t sub_request_id, uint64_t required_id_delta, imquic_moq_request_parameters *parameters); /*! \brief Callback function to be notified about an ACK to a previously sent \c REQUEST_UPDATE message */ void (* request_update_accepted)(imquic_connection *conn, uint64_t request_id, imquic_moq_request_parameters *parameters); /*! \brief Callback function to be notified about incoming errors to a previously \c REQUEST_UPDATE message */ void (* request_update_error)(imquic_connection *conn, uint64_t request_id, imquic_moq_request_error_code error_code, const char *reason, uint64_t retry_interval); /*! \brief Callback function to be notified about incoming \c PUBLISH_DONE messages */ void (* publish_done)(imquic_connection *conn, uint64_t request_id, imquic_moq_pub_done_code status_code, uint64_t streams_count, const char *reason); - /*! \brief Callback function to be notified about incoming \c UNBSUBSCRIBE messages */ + /*! \brief Callback function to be notified about incoming \c UNBSUBSCRIBE messages, or when the bidirectional stream is closed */ void (* incoming_unsubscribe)(imquic_connection *conn, uint64_t request_id); - /*! \brief Callback function to be notified about incoming \c REQUESTS_BLOCKED messages */ + /*! \brief Callback function to be notified about incoming \c REQUESTS_BLOCKED messages + * \note This message was deprecated in v17 */ void (* requests_blocked)(imquic_connection *conn, uint64_t max_request_id); /*! \brief Callback function to be notified about incoming \c SUBSCRIBE_NAMESPACE messages */ - void (* incoming_subscribe_namespace)(imquic_connection *conn, uint64_t request_id, + void (* incoming_subscribe_namespace)(imquic_connection *conn, uint64_t request_id, uint64_t required_id_delta, imquic_moq_namespace *tns, imquic_moq_subscribe_namespace_options subscribe_options, imquic_moq_request_parameters *parameters); /*! \brief Callback function to be notified about incoming \c SUBSCRIBE_NAMESPACE_ACCEPTED messages */ void (* subscribe_namespace_accepted)(imquic_connection *conn, uint64_t request_id, imquic_moq_request_parameters *parameters); /*! \brief Callback function to be notified about incoming \c SUBSCRIBE_NAMESPACE_ERROR messages */ void (* subscribe_namespace_error)(imquic_connection *conn, uint64_t request_id, imquic_moq_request_error_code error_code, const char *reason, uint64_t retry_interval); /*! \brief Callback function to be notified about incoming \c UNSUBSCRIBE_NAMESPACE messages, or when the bidirectional stream is closed */ - void (* incoming_unsubscribe_namespace)(imquic_connection *conn, uint64_t request_id, imquic_moq_namespace *tns); + void (* incoming_unsubscribe_namespace)(imquic_connection *conn, uint64_t request_id); /*! \brief Callback function to be notified about incoming \c NAMESPACE messages */ void (* incoming_namespace)(imquic_connection *conn, uint64_t request_id, imquic_moq_namespace *tns); /*! \brief Callback function to be notified about incoming \c NAMESPACE_DONE messages */ void (* incoming_namespace_done)(imquic_connection *conn, uint64_t request_id, imquic_moq_namespace *tns); + /*! \brief Callback function to be notified about incoming \c PUBLISH_BLOCKED messages */ + void (* incoming_publish_blocked)(imquic_connection *conn, uint64_t request_id, imquic_moq_namespace *tns, imquic_moq_name *tn); /*! \brief Callback function to be notified about incoming \c FETCH messages */ - void (* incoming_standalone_fetch)(imquic_connection *conn, uint64_t request_id, + void (* incoming_standalone_fetch)(imquic_connection *conn, uint64_t request_id, uint64_t required_id_delta, imquic_moq_namespace *tns, imquic_moq_name *tn, imquic_moq_location_range *range, imquic_moq_request_parameters *parameters); - void (* incoming_joining_fetch)(imquic_connection *conn, uint64_t request_id, uint64_t joining_request_id, + void (* incoming_joining_fetch)(imquic_connection *conn, uint64_t request_id, uint64_t required_id_delta, uint64_t joining_request_id, gboolean absolute, uint64_t joining_start, imquic_moq_request_parameters *parameters); - /*! \brief Callback function to be notified about incoming \c FETCH_CANCEL messages */ + /*! \brief Callback function to be notified about incoming \c FETCH_CANCEL messages, or when the bidirectional stream is closed */ void (* incoming_fetch_cancel)(imquic_connection *conn, uint64_t request_id); /*! \brief Callback function to be notified about incoming \c FETCH_ACCEPTED messages */ - void (* fetch_accepted)(imquic_connection *conn, uint64_t request_id, imquic_moq_location *largest, imquic_moq_request_parameters *parameters, GList *track_extensions); + void (* fetch_accepted)(imquic_connection *conn, uint64_t request_id, imquic_moq_location *largest, imquic_moq_request_parameters *parameters, GList *track_properties); /*! \brief Callback function to be notified about incoming \c FETCH_ERROR messages */ void (* fetch_error)(imquic_connection *conn, uint64_t request_id, imquic_moq_request_error_code error_code, const char *reason, uint64_t retry_interval); /*! \brief Callback function to be notified about incoming \c TRACK_STATUS messages */ void (* incoming_track_status)(imquic_connection *conn, uint64_t request_id, imquic_moq_namespace *tns, imquic_moq_name *tn, imquic_moq_request_parameters *parameters); /*! \brief Callback function to be notified about incoming \c TRACK_STATUS_ACCEPTED messages */ - void (* track_status_accepted)(imquic_connection *conn, uint64_t request_id, uint64_t track_alias, imquic_moq_request_parameters *parameters); + void (* track_status_accepted)(imquic_connection *conn, uint64_t request_id, imquic_moq_request_parameters *parameters); /*! \brief Callback function to be notified about incoming \c TRACK_STATUS_ERROR messages */ void (* track_status_error)(imquic_connection *conn, uint64_t request_id, imquic_moq_request_error_code error_code, const char *reason, uint64_t retry_interval); /*! \brief Callback function to be notified about incoming MoQ objects */ void (* incoming_object)(imquic_connection *conn, imquic_moq_object *object); /*! \brief Callback function to be notified about incoming \c GOAWAY messages */ - void (* incoming_goaway)(imquic_connection *conn, const char *uri); + void (* incoming_goaway)(imquic_connection *conn, const char *uri, uint64_t timeout); /*! \brief Callback function to be notified about MoQ connections being closed */ void (* connection_gone)(imquic_connection *conn); } imquic_moq_callbacks; @@ -1414,6 +1356,11 @@ void imquic_moq_datagram_incoming(imquic_connection *conn, uint8_t *bytes, uint6 * @param stream_id The ID of the stream that was reset * @param error_code The error code that was received */ void imquic_moq_reset_stream_incoming(imquic_connection *conn, uint64_t stream_id, uint64_t error_code); +/*! \brief Callback the core invokes when there's an incoming \c STOP_SENDING + * @param conn The imquic_connection instance for which new \c STOP_SENDING is available + * @param stream_id The ID of the stream to stop sending data on + * @param error_code The error code that was received */ +void imquic_moq_stop_sending_incoming(imquic_connection *conn, uint64_t stream_id, uint64_t error_code); /*! \brief Callback the core invokes when an existing MoQ connection is not available anymore * @param conn The imquic_connection instance that is now gone */ void imquic_moq_connection_gone(imquic_connection *conn); @@ -1442,9 +1389,9 @@ void imquic_qlog_moq_message_add_track(json_t *message, imquic_moq_name *track_n /*! \brief Helper to add a stringified array of setup parameters to a message * @note This automatically fills in a property with the specified name * @param message The message object to update - * @param parameters The setup parameters to convert + * @param options The setup options to convert * @param name The name the array should have in the message object */ -void imquic_qlog_moq_message_add_setup_parameters(json_t *message, imquic_moq_setup_parameters *parameters, const char *name); +void imquic_qlog_moq_message_add_setup_options(json_t *message, imquic_moq_setup_options *options, const char *name); /*! \brief Helper to add a stringified array of subscribe parameters to a message * @note This automatically fills in a property with the specified name * @param message The message object to update @@ -1453,11 +1400,11 @@ void imquic_qlog_moq_message_add_setup_parameters(json_t *message, imquic_moq_se * @param name The name the array should have in the message object */ void imquic_qlog_moq_message_add_request_parameters(json_t *message, imquic_moq_version version, imquic_moq_request_parameters *parameters, const char *name); -/*! \brief Helper to add a stringified array of extension headers to a message +/*! \brief Helper to add a stringified array of propertie headers to a message * @param message The message object to update - * @param extensions The list of extensions to convert + * @param properties The list of properties to convert * @param name The name the array should have in the message object */ -void imquic_qlog_moq_message_add_extensions(json_t *message, GList *extensions, const char *name); +void imquic_qlog_moq_message_add_properties(json_t *message, GList *properties, const char *name); /*! \brief Add a \c control_message_created event * @param qlog The imquic_qlog instance to add the event to * @param stream_id The Stream ID used for this message diff --git a/src/internal/network.h b/src/internal/network.h index 82812aa..fab2b81 100644 --- a/src/internal/network.h +++ b/src/internal/network.h @@ -97,6 +97,8 @@ typedef struct imquic_network_endpoint { void (* datagram_incoming)(imquic_connection *conn, uint8_t *bytes, uint64_t length); /*! \brief Callback to invoke when a \c RESET_STREAM arrives on one of the connections handled by this endpoint */ void (* reset_stream_incoming)(imquic_connection *conn, uint64_t stream_id, uint64_t error_code); + /*! \brief Callback to invoke when a \c STOP_SENDING arrives on one of the connections handled by this endpoint */ + void (* stop_sending_incoming)(imquic_connection *conn, uint64_t stream_id, uint64_t error_code); /*! \brief Callback to invoke when new one of the connections handled by this endpoint is closed */ void (* connection_gone)(imquic_connection *conn); /*! \brief Callback to invoke when a client connection attempt fails */ diff --git a/src/moq.c b/src/moq.c index 6f3fe4b..f816000 100644 --- a/src/moq.c +++ b/src/moq.c @@ -4,7 +4,7 @@ * \brief Media Over QUIC (MoQ) stack * \details Implementation of the Media Over QUIC (MoQ) stack as part * of the library itself. At the time of writing, this implements (most - * of) versions from -06 to to -14 of the protocol. + * of) versions from -16 to to -17 of the protocol. * * \note This is the internal implementation of MoQ in the library. You're * still free to only use imquic as the underlying QUIC/WebTransport library, @@ -35,11 +35,36 @@ static GHashTable *moq_sessions = NULL; static imquic_mutex moq_mutex = IMQUIC_MUTEX_INITIALIZER; +/* Buffering of streams/requests, where needed */ +typedef struct imquic_moq_pending_stream { + uint64_t stream_id; + imquic_buffer *buffer; + gboolean complete; +} imquic_moq_pending_stream; +static void imquic_moq_pending_stream_destroy(imquic_moq_pending_stream *stream) { + if(stream != NULL) { + imquic_buffer_destroy(stream->buffer); + g_free(stream); + } +} +static GHashTable *moq_pending_streams = NULL; + +/* MoQ's flavour of varint (introduced in v17) */ +static uint64_t imquic_read_moqint(imquic_moq_version version, uint8_t *bytes, size_t blen, uint8_t *length); +static uint8_t imquic_write_moqint(imquic_moq_version version, uint64_t number, uint8_t *bytes, size_t blen); + +/* Helpers to check and generate GREASE values */ +#define IMQUIC_MOQ_GREASE_BASE 0x7f +#define IMQUIC_MOQ_GREASE_SUM 0x9D +static gboolean imquic_moq_is_grease(uint64_t value); +static uint64_t imquic_moq_random_grease(void); + /* Initialization */ static void imquic_moq_context_destroy(imquic_moq_context *moq); static void imquic_moq_context_free(const imquic_refcount *moq_ref); void imquic_moq_init(void) { moq_sessions = g_hash_table_new_full(NULL, NULL, NULL, (GDestroyNotify)imquic_moq_context_destroy); + moq_pending_streams = g_hash_table_new_full(NULL, NULL, NULL, (GDestroyNotify)g_hash_table_unref); } void imquic_moq_deinit(void) { @@ -47,6 +72,9 @@ void imquic_moq_deinit(void) { if(moq_sessions != NULL) g_hash_table_unref(moq_sessions); moq_sessions = NULL; + if(moq_pending_streams != NULL) + g_hash_table_unref(moq_pending_streams); + moq_pending_streams = NULL; imquic_mutex_unlock(&moq_mutex); } @@ -54,20 +82,10 @@ void imquic_moq_deinit(void) { static imquic_moq_version imquic_moq_version_from_alpn(const char *alpn, imquic_moq_version fallback) { if(alpn == NULL) return fallback; - if(!strcasecmp(alpn, "moq-00")) - return IMQUIC_MOQ_VERSION_ANY_LEGACY; + if(!strcasecmp(alpn, "moq-17")) + return IMQUIC_MOQ_VERSION_17; else if(!strcasecmp(alpn, "moq-16")) return IMQUIC_MOQ_VERSION_16; - else if(!strcasecmp(alpn, "moq-15")) - return IMQUIC_MOQ_VERSION_15; - else if(!strcasecmp(alpn, "moq-14")) - return IMQUIC_MOQ_VERSION_14; - else if(!strcasecmp(alpn, "moq-13")) - return IMQUIC_MOQ_VERSION_13; - else if(!strcasecmp(alpn, "moq-12")) - return IMQUIC_MOQ_VERSION_12; - else if(!strcasecmp(alpn, "moq-11")) - return IMQUIC_MOQ_VERSION_11; /* If we got here, there was no specific ALPN negotiation */ return fallback; } @@ -82,6 +100,9 @@ static gboolean moq_is_request_id_valid(imquic_moq_context *moq, uint64_t reques moq->is_server ? "odd" : "even", moq->is_server ? "server" : "client"); return FALSE; } + /* We only need stricter checks on older versions */ + if(moq->version >= IMQUIC_MOQ_VERSION_17) + return TRUE; if(request_id < moq->next_request_id) { IMQUIC_LOG(IMQUIC_LOG_ERR, "[%s][MoQ] Request ID lower than the next we expected (%"SCNu64" < %"SCNu64")\n", imquic_get_connection_name(moq->conn), request_id, moq->next_request_id); @@ -100,6 +121,9 @@ static gboolean moq_is_request_id_valid(imquic_moq_context *moq, uint64_t reques !moq->is_server ? "odd" : "even", !moq->is_server ? "server" : "client"); return FALSE; } + /* We only need stricter checks on older versions */ + if(moq->version >= IMQUIC_MOQ_VERSION_17) + return TRUE; if(request_id != moq->expected_request_id) { IMQUIC_LOG(IMQUIC_LOG_ERR, "[%s][MoQ] Request ID not the one next we expected (%"SCNu64" != %"SCNu64")\n", imquic_get_connection_name(moq->conn), request_id, moq->expected_request_id); @@ -115,6 +139,49 @@ static gboolean moq_is_request_id_valid(imquic_moq_context *moq, uint64_t reques return TRUE; } +/* Helpers to manage pending streams */ +static void imquic_moq_track_pending_stream(imquic_connection *conn, uint64_t stream_id, uint8_t *bytes, size_t length, gboolean complete) { + imquic_mutex_lock(&moq_mutex); + GHashTable *streams = g_hash_table_lookup(moq_pending_streams, conn); + if(streams == NULL) { + /* No map for this connection yet, create it now */ + streams = g_hash_table_new_full(g_int64_hash, g_int64_equal, + (GDestroyNotify)g_free, (GDestroyNotify)imquic_moq_pending_stream_destroy); + g_hash_table_insert(moq_pending_streams, conn, streams); + } + /* Keep track of the stream data */ + imquic_moq_pending_stream *stream = g_hash_table_lookup(streams, &stream_id); + if(stream == NULL) { + stream = g_malloc0(sizeof(imquic_moq_pending_stream)); + stream->stream_id = stream_id; + stream->buffer = imquic_buffer_create(NULL, 0); + g_hash_table_insert(streams, imquic_uint64_dup(stream_id), stream); + } + imquic_buffer_append(stream->buffer, bytes, length); + stream->complete = complete; + imquic_mutex_unlock(&moq_mutex); +} + +static void imquic_moq_handle_pending_streams(imquic_connection *conn) { + GHashTable *streams = NULL; + imquic_mutex_lock(&moq_mutex); + g_hash_table_steal_extended(moq_pending_streams, conn, + NULL, (gpointer *)&streams); + imquic_mutex_unlock(&moq_mutex); + /* Iterate on the streams list and cleanup */ + if(streams != NULL) { + GHashTableIter iter; + gpointer value; + g_hash_table_iter_init(&iter, streams); + while(g_hash_table_iter_next(&iter, NULL, &value)) { + imquic_moq_pending_stream *stream = value; + imquic_moq_stream_incoming(conn, stream->stream_id, + stream->buffer->bytes, stream->buffer->length, stream->complete); + } + g_hash_table_unref(streams); + } +} + /* Callbacks */ void imquic_moq_new_connection(imquic_connection *conn, void *user_data) { /* Got new connection */ @@ -128,44 +195,61 @@ void imquic_moq_new_connection(imquic_connection *conn, void *user_data) { const char *alpn = imquic_is_connection_webtransport(conn) ? imquic_get_connection_wt_protocol(conn) : imquic_get_connection_alpn(conn); moq->version = imquic_moq_version_from_alpn(alpn, conn->socket->moq_version); - if(alpn == NULL && moq->version == IMQUIC_MOQ_VERSION_ANY) - moq->version = IMQUIC_MOQ_VERSION_ANY_LEGACY; IMQUIC_LOG(IMQUIC_LOG_VERB, "[%s][MoQ] MoQ version: %s (%s)\n", imquic_get_connection_name(conn), imquic_moq_version_str(moq->version), alpn); moq->streams = g_hash_table_new_full(g_int64_hash, g_int64_equal, (GDestroyNotify)g_free, (GDestroyNotify)imquic_moq_stream_destroy); + moq->streams_by_reqid = g_hash_table_new_full(g_int64_hash, g_int64_equal, + (GDestroyNotify)g_free, NULL); moq->subscriptions = g_hash_table_new_full(g_int64_hash, g_int64_equal, (GDestroyNotify)g_free, NULL); moq->subscriptions_by_id = g_hash_table_new_full(g_int64_hash, g_int64_equal, (GDestroyNotify)g_free, (GDestroyNotify)imquic_moq_subscription_destroy); - moq->tns_subscriptions_by_id = g_hash_table_new_full(g_int64_hash, g_int64_equal, - (GDestroyNotify)g_free, NULL); moq->requests = g_hash_table_new_full(g_int64_hash, g_int64_equal, (GDestroyNotify)g_free, NULL); + moq->update_requests = g_hash_table_new_full(g_int64_hash, g_int64_equal, + (GDestroyNotify)g_free, (GDestroyNotify)g_free); moq->buffer = imquic_buffer_create(NULL, 0); imquic_mutex_init(&moq->mutex); imquic_refcount_init(&moq->ref, imquic_moq_context_free); imquic_mutex_lock(&moq_mutex); g_hash_table_insert(moq_sessions, conn, moq); + if(moq->version <= IMQUIC_MOQ_VERSION_16) + g_hash_table_remove(moq_pending_streams, conn); imquic_mutex_unlock(&moq_mutex); - /* If we're a client, let's create a control stream */ - if(!moq->is_server) { + /* Let's check if we need to create a control stream */ + if(moq->version <= IMQUIC_MOQ_VERSION_16) { + /* For older versions, we only open a bidirectional STREAM ourselves + * if we're a client, as we'll need to send a CLIENT_SETUP */ + if(!moq->is_server) { + uint64_t stream_id = 0; + imquic_connection_new_stream_id(conn, TRUE, &stream_id); + moq->control_stream_id = stream_id; + moq->has_control_stream = TRUE; +#ifdef HAVE_QLOG + if(conn->qlog != NULL && conn->qlog->moq) + imquic_moq_qlog_stream_type_set(moq->conn->qlog, TRUE, moq->control_stream_id, "control"); +#endif + } + } else { + /* For newer versions, the role doesn't matter, as we always create + * a unidirectional STREAM to send a SETUP message right away */ uint64_t stream_id = 0; - imquic_connection_new_stream_id(conn, TRUE, &stream_id); + imquic_connection_new_stream_id(conn, FALSE, &stream_id); moq->control_stream_id = stream_id; - moq->has_control_stream = TRUE; + moq->sent_setup = TRUE; #ifdef HAVE_QLOG if(conn->qlog != NULL && conn->qlog->moq) imquic_moq_qlog_stream_type_set(moq->conn->qlog, TRUE, moq->control_stream_id, "control"); #endif } - /* Notify the application: for clients, we'll need it to set role and version */ + /* Notify the application: we may get some info back to use next */ if(conn->socket && conn->socket->callbacks.moq.new_connection) conn->socket->callbacks.moq.new_connection(conn, user_data); /* After the function returns, check if we can do something */ - if(!moq->is_server) { - /* Generate a CLIENT_SETUP */ - imquic_moq_setup_parameters parameters = { 0 }; + if(moq->version <= IMQUIC_MOQ_VERSION_16 && !moq->is_server) { + /* Legacy version, generate a CLIENT_SETUP */ + imquic_moq_setup_options parameters = { 0 }; if(moq->local_max_request_id > 0) { parameters.max_request_id_set = TRUE; parameters.max_request_id = moq->local_max_request_id; @@ -184,35 +268,46 @@ void imquic_moq_new_connection(imquic_connection *conn, void *user_data) { memcpy(parameters.auth_token, moq->auth, moq->authlen); parameters.auth_token_len = moq->authlen; } - if((moq->version >= IMQUIC_MOQ_VERSION_14 && moq->version <= IMQUIC_MOQ_VERSION_MAX) || - moq->version == IMQUIC_MOQ_VERSION_ANY) { - /* FIXME */ - parameters.moqt_implementation_set = TRUE; - g_snprintf(parameters.moqt_implementation, sizeof(parameters.moqt_implementation), "imquic %s", imquic_version_string_full); - } + /* Add the implementation */ + parameters.moqt_implementation_set = TRUE; + g_snprintf(parameters.moqt_implementation, sizeof(parameters.moqt_implementation), "imquic %s", imquic_version_string_full); /* TODO For raw quic connections, we should expose ways to * fill in and use the PATH and ATTRIBUTE parameters as well */ - /* Version is only negotiated here for versions of the draft older than v15 */ - GList *versions = NULL; - if(moq->version == IMQUIC_MOQ_VERSION_ANY_LEGACY || (moq->version >= IMQUIC_MOQ_VERSION_MIN && moq->version <= IMQUIC_MOQ_VERSION_14)) { - if(moq->version == IMQUIC_MOQ_VERSION_ANY_LEGACY) { - /* Offer all newer supported versions */ - versions = g_list_append(versions, GUINT_TO_POINTER(IMQUIC_MOQ_VERSION_14)); - versions = g_list_append(versions, GUINT_TO_POINTER(IMQUIC_MOQ_VERSION_13)); - versions = g_list_append(versions, GUINT_TO_POINTER(IMQUIC_MOQ_VERSION_12)); - versions = g_list_append(versions, GUINT_TO_POINTER(IMQUIC_MOQ_VERSION_11)); - } else { - /* Offer a specific version */ - versions = g_list_append(versions, GUINT_TO_POINTER(moq->version)); + uint8_t buffer[200]; + size_t blen = sizeof(buffer); + size_t cs_len = imquic_moq_add_client_setup(moq, buffer, blen, ¶meters); + imquic_connection_send_on_stream(moq->conn, moq->control_stream_id, + buffer, cs_len, FALSE); + } else if(moq->version >= IMQUIC_MOQ_VERSION_17) { + /* New version, generate a SETUP no matter the role */ + imquic_moq_setup_options parameters = { 0 }; + if(moq->local_max_auth_token_cache_size > 0) { + parameters.max_auth_token_cache_size_set = TRUE; + parameters.max_auth_token_cache_size = moq->local_max_auth_token_cache_size; + } + if(moq->auth != NULL && moq->authlen > 0) { + parameters.auth_token_set = TRUE; + if(moq->authlen > sizeof(parameters.auth_token)) { + IMQUIC_LOG(IMQUIC_MOQ_LOG_HUGE, "[%s][MoQ] Auth token too large (%zu > %zu), it will be truncated\n", + imquic_get_connection_name(moq->conn), moq->authlen, sizeof(parameters.auth_token)); + moq->authlen = sizeof(parameters.auth_token); } + memcpy(parameters.auth_token, moq->auth, moq->authlen); + parameters.auth_token_len = moq->authlen; } + /* Add the implementation */ + parameters.moqt_implementation_set = TRUE; + g_snprintf(parameters.moqt_implementation, sizeof(parameters.moqt_implementation), "imquic %s", imquic_version_string_full); + /* TODO For raw quic connections, we should expose ways to + * fill in and use the PATH and ATTRIBUTE parameters as well */ uint8_t buffer[200]; size_t blen = sizeof(buffer); - size_t cs_len = imquic_moq_add_client_setup(moq, buffer, blen, versions, ¶meters); - g_list_free(versions); + size_t cs_len = imquic_moq_add_setup(moq, buffer, blen, ¶meters); imquic_connection_send_on_stream(moq->conn, moq->control_stream_id, buffer, cs_len, FALSE); } + /* Check if there are streams we kept on hold because we didn't have a context */ + imquic_moq_handle_pending_streams(conn); } void imquic_moq_stream_incoming(imquic_connection *conn, uint64_t stream_id, @@ -225,28 +320,54 @@ void imquic_moq_stream_incoming(imquic_connection *conn, uint64_t stream_id, imquic_moq_context *moq = g_hash_table_lookup(moq_sessions, conn); imquic_mutex_unlock(&moq_mutex); if(moq == NULL) { - IMQUIC_LOG(IMQUIC_LOG_WARN, "[%s][MoQ] Ignoring incoming STREAM data on unknown context\n", + /* FIXME For newer versions, we may get bidirectional stream data + * from requests before we get the SETUP on the unidirectional + * control stream, which means we need to buffer data for later */ + IMQUIC_LOG(IMQUIC_LOG_INFO, "[%s][MoQ] Buffering incoming STREAM data on unknown context\n", imquic_get_connection_name(conn)); + imquic_moq_track_pending_stream(conn, stream_id, bytes, length, complete); return; } if(!moq->has_control_stream) { uint64_t actual_id = 0; gboolean client_initiated = FALSE, bidirectional = FALSE; imquic_parse_stream_id(stream_id, &actual_id, &client_initiated, &bidirectional); - if(!bidirectional) { - /* First stream we get is not a bidirectional control stream */ + /* FIXME Depending on the version, we'll be waiting for the remote + * control stream on either a bidirectional or unidirectional stream */ + if(moq->version <= IMQUIC_MOQ_VERSION_16 && !bidirectional) { + /* Legacy version, we need a bidirectional control stream as a first thing */ IMQUIC_LOG(IMQUIC_LOG_ERR, "[%s][MoQ] Not a bidirectional MoQ control stream\n", imquic_get_connection_name(conn)); return; + } else if(moq->version >= IMQUIC_MOQ_VERSION_17 && bidirectional) { + /* New version, the remote control stream will be unidirectional, + * but we may get some bidirectional streams for requests too + * in the meanwhile: if that happens, queue that data, and we + * will handle it later, once the control stream has been setup */ + IMQUIC_LOG(IMQUIC_LOG_INFO, "[%s][MoQ] Not a unidirectional MoQ control stream, buffering stream %"SCNu64" data\n", + imquic_get_connection_name(conn), stream_id); + imquic_moq_track_pending_stream(conn, stream_id, bytes, length, complete); + return; } moq->has_control_stream = TRUE; - moq->control_stream_id = stream_id; + if(moq->version <= IMQUIC_MOQ_VERSION_16) + moq->control_stream_id = stream_id; + else + moq->remote_control_stream_id = stream_id; #ifdef HAVE_QLOG if(conn->qlog != NULL && conn->qlog->moq) - imquic_moq_qlog_stream_type_set(moq->conn->qlog, FALSE, moq->control_stream_id, "control"); + imquic_moq_qlog_stream_type_set(moq->conn->qlog, FALSE, stream_id, "control"); #endif } imquic_moq_parse_message(moq, stream_id, bytes, length, complete, FALSE); + /* After we've handled this stream, check if there is pending stream + * data we should process now: this can happen in newer MoQ versions + * if we received data from a bidirectional stream (e.g., a request) + * before we received the SETUP on the unidirectional control stream */ + if(g_atomic_int_compare_and_exchange(&moq->check_pending, 1, 0)) { + /* We do, check if there are streams to process */ + imquic_moq_handle_pending_streams(conn); + } } void imquic_moq_datagram_incoming(imquic_connection *conn, uint8_t *bytes, uint64_t length) { @@ -261,6 +382,41 @@ void imquic_moq_datagram_incoming(imquic_connection *conn, uint8_t *bytes, uint6 imquic_moq_parse_message(moq, 0, bytes, length, FALSE, TRUE); } +static void imquic_moq_request_stream_closed(imquic_moq_context *moq, imquic_moq_stream *moq_stream) { + if(moq == NULL || moq_stream == NULL || moq_stream->request_type == 0) + return; + imquic_moq_message_type request_type = moq_stream->request_type; + IMQUIC_LOG(IMQUIC_LOG_INFO, "[%s][MoQ] -- Getting rid of %s '%"SCNu64"'\n", + imquic_get_connection_name(moq->conn), + imquic_moq_message_type_str(request_type, IMQUIC_MOQ_VERSION_17), + moq_stream->request_id); + gboolean request_sender = moq_stream->request_sender; + uint64_t request_id = moq_stream->request_id; + gboolean notify = !request_sender; + imquic_mutex_lock(&moq->mutex); + g_hash_table_remove(moq->streams_by_reqid, &moq_stream->request_id); + g_hash_table_remove(moq->streams, &moq_stream->stream_id); /* */ + imquic_mutex_unlock(&moq->mutex); + /* FIXME Trigger the application callbacks, if needed */ + if(request_type == IMQUIC_MOQ_PUBLISH_NAMESPACE) { + if(notify && moq->conn->socket && moq->conn->socket->callbacks.moq.publish_namespace_done) + moq->conn->socket->callbacks.moq.publish_namespace_done(moq->conn, request_id); + } else if(request_type == IMQUIC_MOQ_SUBSCRIBE_NAMESPACE) { + if(notify && moq->conn->socket && moq->conn->socket->callbacks.moq.incoming_unsubscribe_namespace) + moq->conn->socket->callbacks.moq.incoming_unsubscribe_namespace(moq->conn, request_id); + } else if(request_type == IMQUIC_MOQ_PUBLISH) { + /* FIXME */ + if(notify && moq->conn->socket && moq->conn->socket->callbacks.moq.publish_done) + moq->conn->socket->callbacks.moq.publish_done(moq->conn, request_id, IMQUIC_MOQ_PUBDONE_SUBSCRIPTION_ENDED, 0, "Stream closed"); + } else if(request_type == IMQUIC_MOQ_SUBSCRIBE) { + if(notify && moq->conn->socket && moq->conn->socket->callbacks.moq.incoming_unsubscribe) + moq->conn->socket->callbacks.moq.incoming_unsubscribe(moq->conn, request_id); + } else if(request_type == IMQUIC_MOQ_FETCH) { + if(notify && moq->conn->socket && moq->conn->socket->callbacks.moq.incoming_fetch_cancel) + moq->conn->socket->callbacks.moq.incoming_fetch_cancel(moq->conn, request_id); + } +} + void imquic_moq_reset_stream_incoming(imquic_connection *conn, uint64_t stream_id, uint64_t error_code) { /* We got a RESET_STREAM */ imquic_mutex_lock(&moq_mutex); @@ -278,27 +434,47 @@ void imquic_moq_reset_stream_incoming(imquic_connection *conn, uint64_t stream_i } IMQUIC_LOG(IMQUIC_LOG_INFO, "[%s][MoQ] Got RESET_STREAM for STREAM %"SCNu64": %"SCNu64" (%s)\n", imquic_get_connection_name(conn), stream_id, error_code, imquic_moq_reset_stream_code_str(error_code)); - if(moq->version < IMQUIC_MOQ_VERSION_16 || !moq_stream->subscribe_namespace) { - /* FIXME Not the SUBSCRIBE_NAMESPACE stream, we ignore it for now */ + if(moq_stream->request_type == 0) { + /* FIXME Not a request stream, we ignore it for now */ imquic_mutex_unlock(&moq->mutex); return; } - /* If we got here, a SUBSCRIBE_NAMESPACE bidirectional STREAM was closed */ - IMQUIC_LOG(IMQUIC_LOG_INFO, "[%s][MoQ] -- Getting rid of SUBSCRBE_NAMESPACE '%"SCNu64"'\n", - imquic_get_connection_name(conn), moq_stream->request_id); - gboolean notify = moq_stream->namespace_publisher; - uint64_t request_id = moq_stream->request_id; - g_hash_table_remove(moq->tns_subscriptions_by_id, &moq_stream->request_id); - g_hash_table_remove(moq->streams, &moq_stream->stream_id); /* */ + /* If we got here, a request bidirectional STREAM was closed */ + imquic_moq_request_stream_closed(moq, moq_stream); +} + +void imquic_moq_stop_sending_incoming(imquic_connection *conn, uint64_t stream_id, uint64_t error_code) { + /* We got a STOP_SENDING */ + imquic_mutex_lock(&moq_mutex); + imquic_moq_context *moq = g_hash_table_lookup(moq_sessions, conn); + imquic_mutex_unlock(&moq_mutex); + if(moq == NULL) + return; + imquic_mutex_lock(&moq->mutex); + imquic_moq_stream *moq_stream = g_hash_table_lookup(moq->streams, &stream_id); + if(moq_stream == NULL) { + IMQUIC_LOG(IMQUIC_LOG_WARN, "[%s][MoQ] Got STOP_SENDING for unknown STREAM %"SCNu64": %"SCNu64" (%s)\n", + imquic_get_connection_name(conn), stream_id, error_code, imquic_moq_reset_stream_code_str(error_code)); + imquic_mutex_unlock(&moq->mutex); + return; + } + IMQUIC_LOG(IMQUIC_LOG_INFO, "[%s][MoQ] Got STOP_SENDING for STREAM %"SCNu64": %"SCNu64" (%s)\n", + imquic_get_connection_name(conn), stream_id, error_code, imquic_moq_reset_stream_code_str(error_code)); imquic_mutex_unlock(&moq->mutex); - if(notify && moq->conn->socket && moq->conn->socket->callbacks.moq.incoming_unsubscribe_namespace) - moq->conn->socket->callbacks.moq.incoming_unsubscribe_namespace(moq->conn, request_id, NULL); + if(moq_stream->request_type == 0) { + /* FIXME Not a request stream, we ignore it for now */ + imquic_mutex_unlock(&moq->mutex); + return; + } + /* If we got here, a request bidirectional STREAM was closed */ + imquic_moq_request_stream_closed(moq, moq_stream); } void imquic_moq_connection_gone(imquic_connection *conn) { /* Connection was closed */ imquic_mutex_lock(&moq_mutex); gboolean removed = g_hash_table_remove(moq_sessions, conn); + g_hash_table_remove(moq_pending_streams, conn); imquic_mutex_unlock(&moq_mutex); if(conn->socket && conn->socket->callbacks.moq.connection_gone) conn->socket->callbacks.moq.connection_gone(conn); @@ -325,19 +501,21 @@ static void imquic_moq_context_free(const imquic_refcount *moq_ref) { g_hash_table_unref(moq->subscriptions); if(moq->subscriptions_by_id) g_hash_table_unref(moq->subscriptions_by_id); - if(moq->tns_subscriptions_by_id) - g_hash_table_unref(moq->tns_subscriptions_by_id); + if(moq->streams_by_reqid) + g_hash_table_unref(moq->streams_by_reqid); if(moq->requests) g_hash_table_unref(moq->requests); + if(moq->update_requests) + g_hash_table_unref(moq->update_requests); imquic_buffer_destroy(moq->buffer); g_free(moq); } -static void imquic_moq_object_extension_free(imquic_moq_object_extension *extension) { - if(extension != NULL) { - if(extension->value.data.buffer != NULL) - g_free(extension->value.data.buffer); - g_free(extension); +static void imquic_moq_property_free(imquic_moq_property *property) { + if(property != NULL) { + if(property->value.data.buffer != NULL) + g_free(property->value.data.buffer); + g_free(property); } } @@ -358,8 +536,8 @@ const char *imquic_moq_error_code_str(imquic_moq_error_code code) { return "Duplicate Track Alias"; case IMQUIC_MOQ_KEYVALUE_FORMATTING_ERROR: return "Key-Value Formatting Error"; - case IMQUIC_MOQ_TOO_MANY_REQUESTS: - return "Too Many Requests"; + case IMQUIC_MOQ_INVALID_REQUIRED_REQUEST_ID: + return "Invalid Required Request ID"; case IMQUIC_MOQ_INVALID_PATH: return "Invalid Path"; case IMQUIC_MOQ_MALFORMED_PATH: @@ -396,113 +574,42 @@ const char *imquic_moq_error_code_str(imquic_moq_error_code code) { const char *imquic_moq_request_error_code_str(imquic_moq_request_error_code code) { switch(code) { case IMQUIC_MOQ_REQERR_INTERNAL_ERROR: - return "INTERNAL_ERROR"; + return "Internal Error"; case IMQUIC_MOQ_REQERR_UNAUTHORIZED: - return "UNAUTHORIZED"; + return "Unauthorized"; case IMQUIC_MOQ_REQERR_TIMEOUT: - return "TIMEOUT"; + return "Timeout"; case IMQUIC_MOQ_REQERR_NOT_SUPPORTED: - return "NOT_SUPPORTED"; + return "Not Suppoered"; case IMQUIC_MOQ_REQERR_MALFORMED_AUTH_TOKEN: - return "MALFORMED_AUTH_TOKEN"; + return "Malformed Auth Token"; case IMQUIC_MOQ_REQERR_EXPIRED_AUTH_TOKEN: - return "EXPIRED_AUTH_TOKEN"; + return "Expired Auth Token"; + case IMQUIC_MOQ_REQERR_GOING_AWAY: + return "Going Away"; + case IMQUIC_MOQ_REQERR_EXCESSIVE_LOAD: + return "Excessive Load"; case IMQUIC_MOQ_REQERR_DOES_NOT_EXIST: - return "DOES_NOT_EXIST"; + return "Does Not Exist"; case IMQUIC_MOQ_REQERR_INVALID_RANGE: - return "INVALID_RANGE"; + return "Invalid Range"; case IMQUIC_MOQ_REQERR_MALFORMED_TRACK: - return "MALFORMED_TRACK"; + return "Malformed Track"; case IMQUIC_MOQ_REQERR_DUPLICATE_SUBSCRIPTION: - return "DUPLICATE_SUBSCRIPTION"; + return "Duplicate Subscription"; case IMQUIC_MOQ_REQERR_UNINTERESTED: - return "UNINTERESTED"; + return "Uninterested"; case IMQUIC_MOQ_REQERR_PREFIX_OVERLAP: - return "PREFIX_OVERLAP"; + return "Prefix Overlap"; + case IMQUIC_MOQ_REQERR_NAMESPACE_TOO_LARGE: + return "Namespace Too Large"; case IMQUIC_MOQ_REQERR_INVALID_JOINING_REQUEST_ID: - return "INVALID_JOINING_REQUEST_ID"; - case IMQUIC_MOQ_REQERR_UNKNOWN_STATUS_IN_RANGE: - return "UNKNOWN_STATUS_IN_RANGE"; + return "Invalid Joining Request ID"; default: break; } return NULL; } -imquic_moq_legacy_error_code imquic_moq_request_error_code_to_legacy(imquic_moq_version version, imquic_moq_request_error_code code) { - if(version >= IMQUIC_MOQ_VERSION_15) - return (imquic_moq_legacy_error_code)code; - switch(code) { - /* Same code */ - case IMQUIC_MOQ_REQERR_INTERNAL_ERROR: - case IMQUIC_MOQ_REQERR_UNAUTHORIZED: - case IMQUIC_MOQ_REQERR_TIMEOUT: - case IMQUIC_MOQ_REQERR_NOT_SUPPORTED: - return (imquic_moq_legacy_error_code)code; - /* Error code was different up to v14 */ - case IMQUIC_MOQ_REQERR_MALFORMED_AUTH_TOKEN: - return IMQUIC_MOQ_OLDERR_MALFORMED_AUTH_TOKEN; - case IMQUIC_MOQ_REQERR_EXPIRED_AUTH_TOKEN: - return IMQUIC_MOQ_OLDERR_EXPIRED_AUTH_TOKEN; - case IMQUIC_MOQ_REQERR_DOES_NOT_EXIST: - return IMQUIC_MOQ_OLDERR_TRACK_DOES_NOT_EXIST; - case IMQUIC_MOQ_REQERR_INVALID_RANGE: - return IMQUIC_MOQ_OLDERR_INVALID_RANGE; - case IMQUIC_MOQ_REQERR_MALFORMED_TRACK: - return IMQUIC_MOQ_OLDERR_MALFORMED_TRACK; - case IMQUIC_MOQ_REQERR_UNINTERESTED: - return IMQUIC_MOQ_OLDERR_UNINTERESTED; - case IMQUIC_MOQ_REQERR_INVALID_JOINING_REQUEST_ID: - return IMQUIC_MOQ_OLDERR_INVALID_JOINING_REQUEST_ID; - case IMQUIC_MOQ_REQERR_UNKNOWN_STATUS_IN_RANGE: - return IMQUIC_MOQ_OLDERR_UNKNOWN_STATUS_IN_RANGE; - /* New error codes with no mapping to old ones */ - case IMQUIC_MOQ_REQERR_PREFIX_OVERLAP: - case IMQUIC_MOQ_REQERR_DUPLICATE_SUBSCRIPTION: - default: - IMQUIC_LOG(IMQUIC_LOG_WARN, "Can't translate new error code %s (%s) to old error code\n", - imquic_moq_request_error_code_str(code), imquic_moq_version_str(version)); - break; - } - /* As a fallback, we return a generic internal error */ - return IMQUIC_MOQ_OLDERR_INTERNAL_ERROR; -} - -imquic_moq_request_error_code imquic_moq_request_error_code_from_legacy(imquic_moq_version version, imquic_moq_legacy_error_code code) { - if(version >= IMQUIC_MOQ_VERSION_15) - return (imquic_moq_request_error_code)code; - switch(code) { - /* Same code */ - case IMQUIC_MOQ_OLDERR_INTERNAL_ERROR: - case IMQUIC_MOQ_OLDERR_UNAUTHORIZED: - case IMQUIC_MOQ_OLDERR_TIMEOUT: - case IMQUIC_MOQ_OLDERR_NOT_SUPPORTED: - return (imquic_moq_request_error_code)code; - case IMQUIC_MOQ_OLDERR_TRACK_DOES_NOT_EXIST: - /* Note: there's actually a conflit here (it's also the - * old UNINTERESTED), but this is more common/important */ - return IMQUIC_MOQ_REQERR_DOES_NOT_EXIST; - case IMQUIC_MOQ_OLDERR_INVALID_RANGE: - return IMQUIC_MOQ_REQERR_INVALID_RANGE; - case IMQUIC_MOQ_OLDERR_INVALID_JOINING_REQUEST_ID: - return IMQUIC_MOQ_REQERR_INVALID_JOINING_REQUEST_ID; - case IMQUIC_MOQ_OLDERR_UNKNOWN_STATUS_IN_RANGE: - return IMQUIC_MOQ_REQERR_UNKNOWN_STATUS_IN_RANGE; - case IMQUIC_MOQ_OLDERR_MALFORMED_TRACK: - return IMQUIC_MOQ_REQERR_MALFORMED_TRACK; - case IMQUIC_MOQ_OLDERR_MALFORMED_AUTH_TOKEN: - return IMQUIC_MOQ_REQERR_MALFORMED_AUTH_TOKEN; - case IMQUIC_MOQ_OLDERR_EXPIRED_AUTH_TOKEN: - return IMQUIC_MOQ_REQERR_EXPIRED_AUTH_TOKEN; - /* No mapping or multiple (ambiguous) mappings */ - default: - IMQUIC_LOG(IMQUIC_LOG_WARN, "Can't translate (%s) old error code '%02x' to new request error codes\n", - imquic_moq_version_str(version), code); - break; - } - /* As a fallback, we return a generic internal error */ - return IMQUIC_MOQ_REQERR_INTERNAL_ERROR; -} - const char *imquic_moq_pub_done_code_str(imquic_moq_pub_done_code code) { switch(code) { case IMQUIC_MOQ_PUBDONE_INTERNAL_ERROR: @@ -519,10 +626,12 @@ const char *imquic_moq_pub_done_code_str(imquic_moq_pub_done_code code) { return "Expired"; case IMQUIC_MOQ_PUBDONE_TOO_FAR_BEHIND: return "Too Far Behind"; - case IMQUIC_MOQ_PUBDONE_MALFORMED_TRACK: - return "Malformed Track"; case IMQUIC_MOQ_PUBDONE_UPDATE_FAILED: return "Update Failed"; + case IMQUIC_MOQ_PUBDONE_EXCESSIVE_LOAD: + return "Excessive Load"; + case IMQUIC_MOQ_PUBDONE_MALFORMED_TRACK: + return "Malformed Track"; default: break; } return NULL; @@ -540,6 +649,10 @@ const char *imquic_moq_reset_stream_code_str(imquic_moq_reset_stream_code code) return "Session Closed"; case IMQUIC_MOQ_RESET_UNKNOWN_OBJECT_STATUS: return "Unknown Object Status"; + case IMQUIC_MOQ_RESET_TOO_FAR_BEHIND: + return "Too Far Behind"; + case IMQUIC_MOQ_RESET_EXCESSIVE_LOAD: + return "Excessive Load"; case IMQUIC_MOQ_RESET_MALFORMED_TRACK: return "Malformed Track"; default: break; @@ -548,303 +661,143 @@ const char *imquic_moq_reset_stream_code_str(imquic_moq_reset_stream_code code) } const char *imquic_moq_message_type_str(imquic_moq_message_type type, imquic_moq_version version) { - if(version <= IMQUIC_MOQ_VERSION_14) { - switch(type) { - case IMQUIC_MOQ_SUBSCRIBE: - return "SUBSCRIBE"; - case IMQUIC_MOQ_SUBSCRIBE_OK: - return "SUBSCRIBE_OK"; - case IMQUIC_MOQ_SUBSCRIBE_ERROR: - return "SUBSCRIBE_ERROR"; - case IMQUIC_MOQ_REQUEST_UPDATE: - return "SUBSCRIBE_UPDATE"; - case IMQUIC_MOQ_PUBLISH_NAMESPACE: - return "PUBLISH_NAMESPACE"; - case IMQUIC_MOQ_PUBLISH_NAMESPACE_OK: - return "PUBLISH_NAMESPACE_OK"; - case IMQUIC_MOQ_PUBLISH_NAMESPACE_ERROR: - return "PUBLISH_NAMESPACE_ERROR"; - case IMQUIC_MOQ_PUBLISH_NAMESPACE_DONE: - return "PUBLISH_NAMESPACE_DONE"; - case IMQUIC_MOQ_UNSUBSCRIBE: - return "UNSUBSCRIBE"; - case IMQUIC_MOQ_PUBLISH_DONE: - return "PUBLISH_DONE"; - case IMQUIC_MOQ_PUBLISH_NAMESPACE_CANCEL: - return "PUBLISH_NAMESPACE_CANCEL"; - case IMQUIC_MOQ_TRACK_STATUS: - return "TRACK_STATUS"; - case IMQUIC_MOQ_TRACK_STATUS_OK: - return "TRACK_STATUS_OK"; - case IMQUIC_MOQ_TRACK_STATUS_ERROR: - return "TRACK_STATUS_ERROR"; - case IMQUIC_MOQ_GOAWAY: - return "GOAWAY"; - case IMQUIC_MOQ_SUBSCRIBE_NAMESPACE: - return "SUBSCRIBE_NAMESPACE"; - case IMQUIC_MOQ_SUBSCRIBE_NAMESPACE_OK: - return "SUBSCRIBE_NAMESPACE_OK"; - case IMQUIC_MOQ_SUBSCRIBE_NAMESPACE_ERROR: - return "SUBSCRIBE_NAMESPACE_ERROR"; - case IMQUIC_MOQ_UNSUBSCRIBE_NAMESPACE: - return "UNSUBSCRIBE_NAMESPACE"; - case IMQUIC_MOQ_MAX_REQUEST_ID: - return "MAX_REQUEST_ID"; - case IMQUIC_MOQ_REQUESTS_BLOCKED: - return "REQUESTS_BLOCKED"; - case IMQUIC_MOQ_FETCH: - return "FETCH"; - case IMQUIC_MOQ_FETCH_CANCEL: - return "FETCH_CANCEL"; - case IMQUIC_MOQ_FETCH_OK: - return "FETCH_OK"; - case IMQUIC_MOQ_FETCH_ERROR: - return "FETCH_ERROR"; - case IMQUIC_MOQ_CLIENT_SETUP: - return "CLIENT_SETUP"; - case IMQUIC_MOQ_SERVER_SETUP: - return "SERVER_SETUP"; - case IMQUIC_MOQ_PUBLISH: - return "PUBLISH"; - case IMQUIC_MOQ_PUBLISH_OK: - return "PUBLISH_OK"; - case IMQUIC_MOQ_PUBLISH_ERROR: - return "PUBLISH_ERROR"; - default: break; - } - } else { - switch(type) { - case IMQUIC_MOQ_REQUEST_OK: - return "REQUEST_OK"; - case IMQUIC_MOQ_REQUEST_ERROR: - return "REQUEST_ERROR"; - case IMQUIC_MOQ_SUBSCRIBE: - return "SUBSCRIBE"; - case IMQUIC_MOQ_SUBSCRIBE_OK: - return "SUBSCRIBE_OK"; - case IMQUIC_MOQ_REQUEST_UPDATE: - return (version == IMQUIC_MOQ_VERSION_15 ? "SUBSCRIBE_UPDATE" : "REQUEST_UPDATE"); - case IMQUIC_MOQ_PUBLISH_NAMESPACE: - return "PUBLISH_NAMESPACE"; - case IMQUIC_MOQ_PUBLISH_NAMESPACE_DONE: - return "PUBLISH_NAMESPACE_DONE"; - case IMQUIC_MOQ_UNSUBSCRIBE: - return "UNSUBSCRIBE"; - case IMQUIC_MOQ_PUBLISH_DONE: - return "PUBLISH_DONE"; - case IMQUIC_MOQ_PUBLISH_NAMESPACE_CANCEL: - return "PUBLISH_NAMESPACE_CANCEL"; - case IMQUIC_MOQ_TRACK_STATUS: - return "TRACK_STATUS"; - case IMQUIC_MOQ_GOAWAY: - return "GOAWAY"; - case IMQUIC_MOQ_SUBSCRIBE_NAMESPACE: - return "SUBSCRIBE_NAMESPACE"; - case IMQUIC_MOQ_UNSUBSCRIBE_NAMESPACE: - return "UNSUBSCRIBE_NAMESPACE"; - case IMQUIC_MOQ_NAMESPACE: - return (version >= IMQUIC_MOQ_VERSION_16 ? "NAMESPACE" : NULL); - case IMQUIC_MOQ_NAMESPACE_DONE: - return (version >= IMQUIC_MOQ_VERSION_16 ? "NAMESPACE_DONE" : NULL); - case IMQUIC_MOQ_MAX_REQUEST_ID: - return "MAX_REQUEST_ID"; - case IMQUIC_MOQ_REQUESTS_BLOCKED: - return "REQUESTS_BLOCKED"; - case IMQUIC_MOQ_FETCH: - return "FETCH"; - case IMQUIC_MOQ_FETCH_CANCEL: - return "FETCH_CANCEL"; - case IMQUIC_MOQ_FETCH_OK: - return "FETCH_OK"; - case IMQUIC_MOQ_CLIENT_SETUP: - return "CLIENT_SETUP"; - case IMQUIC_MOQ_SERVER_SETUP: - return "SERVER_SETUP"; - case IMQUIC_MOQ_PUBLISH: - return "PUBLISH"; - case IMQUIC_MOQ_PUBLISH_OK: - return "PUBLISH_OK"; - default: break; - } + switch(type) { + case IMQUIC_MOQ_REQUEST_OK: + return "REQUEST_OK"; + case IMQUIC_MOQ_REQUEST_ERROR: + return "REQUEST_ERROR"; + case IMQUIC_MOQ_SUBSCRIBE: + return "SUBSCRIBE"; + case IMQUIC_MOQ_SUBSCRIBE_OK: + return "SUBSCRIBE_OK"; + case IMQUIC_MOQ_REQUEST_UPDATE: + return "REQUEST_UPDATE"; + case IMQUIC_MOQ_PUBLISH_NAMESPACE: + return "PUBLISH_NAMESPACE"; + case IMQUIC_MOQ_PUBLISH_NAMESPACE_DONE: + return "PUBLISH_NAMESPACE_DONE"; + case IMQUIC_MOQ_UNSUBSCRIBE: + return "UNSUBSCRIBE"; + case IMQUIC_MOQ_PUBLISH_DONE: + return "PUBLISH_DONE"; + case IMQUIC_MOQ_PUBLISH_NAMESPACE_CANCEL: + return "PUBLISH_NAMESPACE_CANCEL"; + case IMQUIC_MOQ_TRACK_STATUS: + return "TRACK_STATUS"; + case IMQUIC_MOQ_GOAWAY: + return "GOAWAY"; + case IMQUIC_MOQ_SUBSCRIBE_NAMESPACE: + return "SUBSCRIBE_NAMESPACE"; + case IMQUIC_MOQ_NAMESPACE: + return "NAMESPACE"; + case IMQUIC_MOQ_NAMESPACE_DONE: + return "NAMESPACE_DONE"; + case IMQUIC_MOQ_PUBLISH_BLOCKED: + return "PUBLISH_BLOCKED"; + case IMQUIC_MOQ_MAX_REQUEST_ID: + return "MAX_REQUEST_ID"; + case IMQUIC_MOQ_REQUESTS_BLOCKED: + return "REQUESTS_BLOCKED"; + case IMQUIC_MOQ_FETCH: + return "FETCH"; + case IMQUIC_MOQ_FETCH_CANCEL: + return "FETCH_CANCEL"; + case IMQUIC_MOQ_FETCH_OK: + return "FETCH_OK"; + case IMQUIC_MOQ_CLIENT_SETUP: + return "CLIENT_SETUP"; + case IMQUIC_MOQ_SERVER_SETUP: + return "SERVER_SETUP"; + case IMQUIC_MOQ_SETUP: + return "SETUP"; + case IMQUIC_MOQ_PUBLISH: + return "PUBLISH"; + case IMQUIC_MOQ_PUBLISH_OK: + return "PUBLISH_OK"; + default: break; } return NULL; } -gboolean imquic_moq_is_datagram_message_type_valid(imquic_moq_version version, uint8_t type) { - if(version == IMQUIC_MOQ_VERSION_11) { - return (type <= 0x03); - } else if(version == IMQUIC_MOQ_VERSION_12 || version == IMQUIC_MOQ_VERSION_13) { - return (type <= 0x05); - } else if(version == IMQUIC_MOQ_VERSION_14) { - return (type <= 0x07 || type == 0x20 || type == 0x21); - } else { - return (type <= 0x0F || (type >= 0x20 && type <= 0x2D)); +const char *imquic_media_stream_request_state_str(imquic_media_stream_request_state state) { + switch(state) { + case IMQUIC_MOQ_REQUEST_STATE_NEW: + return "New"; + case IMQUIC_MOQ_REQUEST_STATE_SENT: + return "Sent"; + case IMQUIC_MOQ_REQUEST_STATE_OK: + return "OK"; + case IMQUIC_MOQ_REQUEST_STATE_ERROR: + return "Error"; + case IMQUIC_MOQ_REQUEST_STATE_UPDATE_SENT: + return "Update Sent"; + case IMQUIC_MOQ_REQUEST_STATE_DONE: + return "Done"; + default: break; } - return FALSE; + return NULL; +} + +gboolean imquic_moq_is_datagram_message_type_valid(imquic_moq_version version, uint8_t type) { + return (type <= 0x0F || (type >= 0x20 && type <= 0x2D)); } uint8_t imquic_moq_datagram_message_type_return(imquic_moq_version version, - gboolean payload, gboolean ext, gboolean eog, gboolean oid, gboolean priority) { - if(version == IMQUIC_MOQ_VERSION_11) { - /* v11 */ - if(payload) - return ext ? 0x01 : 0x00; - else - return ext ? 0x03 : 0x02; - } else if(version == IMQUIC_MOQ_VERSION_12 || version == IMQUIC_MOQ_VERSION_13) { - /* v12 and v13 */ - if(payload) { - uint8_t type = 0x00; - if(eog) - type |= 0x02; - if(ext) - type |= 0x01; - return type; - } else { - return ext ? 0x05 : 0x04; - } - } else if(version == IMQUIC_MOQ_VERSION_14) { - /* v14 */ - if(payload) { - uint8_t type = 0x00; - if(eog) - type |= 0x02; - if(ext) - type |= 0x01; - if(!oid) - type |= 0x04; - return type; - } else { - return ext ? 0x21 : 0x20; - } - } else { - /* v15 and later */ - uint8_t type = payload ? 0x00 : 0x20; - if(!payload) - eog = FALSE; - if(eog) - type |= 0x02; - if(ext) - type |= 0x01; - if(!oid) - type |= 0x04; - if(!priority) - type |= 0x08; - return type; - } + gboolean payload, gboolean prop, gboolean eog, gboolean oid, gboolean priority) { + uint8_t type = payload ? 0x00 : 0x20; + if(!payload) + eog = FALSE; + if(eog) + type |= 0x02; + if(prop) + type |= 0x01; + if(!oid) + type |= 0x04; + if(!priority) + type |= 0x08; + return type; } void imquic_moq_datagram_message_type_parse(imquic_moq_version version, uint8_t type, - gboolean *payload, gboolean *ext, gboolean *eog, gboolean *oid, gboolean *priority, gboolean *violation) { + gboolean *payload, gboolean *prop, gboolean *eog, gboolean *oid, gboolean *priority, gboolean *violation) { if(oid) *oid = TRUE; if(priority) *priority = TRUE; - if(version == IMQUIC_MOQ_VERSION_11) { - /* v11 */ - if(payload) - *payload = (type == 0x00 || type == 0x01); - if(ext) - *ext = (type == 0x01 || type == 0x03); - if(violation) - *violation = (type > 0x03); - } else if(version == IMQUIC_MOQ_VERSION_12 || version == IMQUIC_MOQ_VERSION_13) { - /* v12 and v13 */ - if(payload) - *payload = (type <= 0x03); - if(ext) - *ext = (type & 0x01); - if(eog) - *eog = (type & 0x02); - if(violation) - *violation = (type > 0x05); - } else if(version == IMQUIC_MOQ_VERSION_14) { - /* v14 */ - if(payload) - *payload = (type <= 0x07); - if(ext) - *ext = (type & 0x01); - if(eog) - *eog = (type & 0x02); - if(oid) - *oid = !(type & 0x04); - if(violation) - *violation = ((type > 0x05 && type < 0x20) || (type > 0x21)); - } else { - /* v15 and later */ - if(payload) - *payload = !(type & 0x20); - if(ext) - *ext = (type & 0x01); - if(eog) - *eog = (type & 0x02); - if(oid) - *oid = !(type & 0x04); - if(priority) - *priority = !(type & 0x08); - if(violation) - *violation = ((type > 0x0F && type < 0x20) || type > 0x2D || (type >= 0x20 && (type & 0x02))); - } + /* v15 and later */ + if(payload) + *payload = !(type & 0x20); + if(prop) + *prop = (type & 0x01); + if(eog) + *eog = (type & 0x02); + if(oid) + *oid = !(type & 0x04); + if(priority) + *priority = !(type & 0x08); + if(violation) + *violation = ((type > 0x0F && type < 0x20) || type > 0x2D || (type >= 0x20 && (type & 0x02))); } const char *imquic_moq_datagram_message_type_str(uint8_t type, imquic_moq_version version) { - if(version == IMQUIC_MOQ_VERSION_11) { - if(type == 0x00 || type == 0x01) - return "OBJECT_DATAGRAM"; - else if(type == 0x02 || type == 0x03) - return "OBJECT_DATAGRAM_STATUS"; - } else if(version == IMQUIC_MOQ_VERSION_12 || version == IMQUIC_MOQ_VERSION_13) { - if(type <= 0x03) - return "OBJECT_DATAGRAM"; - else if(type == 0x04 || type == 0x05) - return "OBJECT_DATAGRAM_STATUS"; - } else if(version == IMQUIC_MOQ_VERSION_14) { - if(type <= 0x07) - return "OBJECT_DATAGRAM"; - else if(type == 0x20 || type == 0x21) - return "OBJECT_DATAGRAM_STATUS"; - } else { - if(type <= 0x0F) - return "OBJECT_DATAGRAM"; - else if(type >= 0x20 && type <= 0x2D) - return "OBJECT_DATAGRAM_STATUS"; - } + if(type <= 0x0F) + return "OBJECT_DATAGRAM"; + else if(type >= 0x20 && type <= 0x2D) + return "OBJECT_DATAGRAM_STATUS"; return NULL; } gboolean imquic_moq_is_data_message_type_valid(imquic_moq_version version, uint8_t type) { if(type == IMQUIC_MOQ_FETCH_HEADER) return TRUE; - if(version == IMQUIC_MOQ_VERSION_11) { - if(type == IMQUIC_MOQ_SUBGROUP_HEADER_NOSGID0_NOEXT_v11 || type == IMQUIC_MOQ_SUBGROUP_HEADER_NOSGID0_v11 || - type == IMQUIC_MOQ_SUBGROUP_HEADER_NOSGID_NOEXT_v11 || type == IMQUIC_MOQ_SUBGROUP_HEADER_NOSGID_v11 || - type == IMQUIC_MOQ_SUBGROUP_HEADER_NOEXT_v11 || type == IMQUIC_MOQ_SUBGROUP_HEADER_v11) - return TRUE; - } else { - if((type >= IMQUIC_MOQ_SUBGROUP_HEADER_RANGE1_MIN && type <= IMQUIC_MOQ_SUBGROUP_HEADER_RANGE1_MAX) || - (version >= IMQUIC_MOQ_VERSION_15 && (type >= IMQUIC_MOQ_SUBGROUP_HEADER_RANGE2_MIN && type <= IMQUIC_MOQ_SUBGROUP_HEADER_RANGE2_MAX))) - return TRUE; - } + if((type >= IMQUIC_MOQ_SUBGROUP_HEADER_RANGE1_MIN && type <= IMQUIC_MOQ_SUBGROUP_HEADER_RANGE1_MAX) || + (type >= IMQUIC_MOQ_SUBGROUP_HEADER_RANGE2_MIN && type <= IMQUIC_MOQ_SUBGROUP_HEADER_RANGE2_MAX)) + return TRUE; return FALSE; } uint8_t imquic_moq_data_message_type_from_subgroup_header(imquic_moq_version version, - gboolean subgroup, gboolean sgid0, gboolean ext, gboolean eog, gboolean priority) { - if(version == IMQUIC_MOQ_VERSION_11) { - /* v11 */ - if(!subgroup && sgid0 && !ext) - return IMQUIC_MOQ_SUBGROUP_HEADER_NOSGID0_NOEXT_v11; - else if(!subgroup && sgid0 && ext) - return IMQUIC_MOQ_SUBGROUP_HEADER_NOSGID0_v11; - else if(!subgroup && !sgid0 && !ext) - return IMQUIC_MOQ_SUBGROUP_HEADER_NOSGID_NOEXT_v11; - else if(!subgroup && !sgid0 && ext) - return IMQUIC_MOQ_SUBGROUP_HEADER_NOSGID_v11; - else if(subgroup && !ext) - return IMQUIC_MOQ_SUBGROUP_HEADER_NOEXT_v11; - return IMQUIC_MOQ_SUBGROUP_HEADER_v11; - } - /* If we're here, we're on v12 or later */ + gboolean subgroup, gboolean sgid0, gboolean prop, gboolean eog, gboolean priority) { uint8_t type = 0; if(subgroup) { sgid0 = FALSE; @@ -852,122 +805,66 @@ uint8_t imquic_moq_data_message_type_from_subgroup_header(imquic_moq_version ver } if(sgid0) type |= 0x02; - if(ext) + if(prop) type |= 0x01; if(eog) type |= 0x08; - if(version >= IMQUIC_MOQ_VERSION_15) - priority = TRUE; type |= (priority ? 0x10 : 0x30); return type; } void imquic_moq_data_message_type_to_subgroup_header(imquic_moq_version version, uint8_t type, - gboolean *subgroup, gboolean *sgid0, gboolean *ext, gboolean *eog, gboolean *priority, gboolean *violation) { - if(version == IMQUIC_MOQ_VERSION_11) { - /* v11 */ - if(subgroup) - *subgroup = (type == IMQUIC_MOQ_SUBGROUP_HEADER_NOEXT_v11 || type == IMQUIC_MOQ_SUBGROUP_HEADER_v11); - if(sgid0) - *sgid0 = (type == IMQUIC_MOQ_SUBGROUP_HEADER_NOSGID0_NOEXT_v11 || type == IMQUIC_MOQ_SUBGROUP_HEADER_NOSGID0_v11); - if(ext) - *ext = (type == IMQUIC_MOQ_SUBGROUP_HEADER_NOSGID0_v11 || type == IMQUIC_MOQ_SUBGROUP_HEADER_NOSGID_v11 || type == IMQUIC_MOQ_SUBGROUP_HEADER_v11); - if(priority) - *priority = TRUE; - } else { - /* v12 and later */ - uint8_t base = 0x10; - if(type >= IMQUIC_MOQ_SUBGROUP_HEADER_RANGE1_MIN && type <= IMQUIC_MOQ_SUBGROUP_HEADER_RANGE1_MAX) { - base = 0x10; - } else if(type >= IMQUIC_MOQ_SUBGROUP_HEADER_RANGE2_MIN && type <= IMQUIC_MOQ_SUBGROUP_HEADER_RANGE2_MAX) { - if(version < IMQUIC_MOQ_VERSION_15) { - /* This range is only allowed starting from v15 */ - if(violation) - *violation = TRUE; - return; - } - base = 0x30; - } - uint8_t bitmask = type - base; - if(bitmask == 0x06 || bitmask == 0x07) { - /* If these bits are set, it's a protocol violation */ - if(violation) - *violation = TRUE; - return; - } - if(priority) - *priority = (base == 0x10); - if(subgroup) - *subgroup = (bitmask & 0x04); - if(sgid0) - *sgid0 = (bitmask & 0x02); - if(ext) - *ext = (bitmask & 0x01); - if(eog) - *eog = (bitmask & 0x08); + gboolean *subgroup, gboolean *sgid0, gboolean *prop, gboolean *eog, gboolean *priority, gboolean *violation) { + uint8_t base = 0x10; + if(type >= IMQUIC_MOQ_SUBGROUP_HEADER_RANGE1_MIN && type <= IMQUIC_MOQ_SUBGROUP_HEADER_RANGE1_MAX) { + base = 0x10; + } else if(type >= IMQUIC_MOQ_SUBGROUP_HEADER_RANGE2_MIN && type <= IMQUIC_MOQ_SUBGROUP_HEADER_RANGE2_MAX) { + base = 0x30; + } + uint8_t bitmask = type - base; + if(bitmask == 0x06 || bitmask == 0x07) { + /* If these bits are set, it's a protocol violation */ + if(violation) + *violation = TRUE; + return; } + if(priority) + *priority = (base == 0x10); + if(subgroup) + *subgroup = (bitmask & 0x04); + if(sgid0) + *sgid0 = (bitmask & 0x02); + if(prop) + *prop = (bitmask & 0x01); + if(eog) + *eog = (bitmask & 0x08); } const char *imquic_moq_data_message_type_str(imquic_moq_data_message_type type, imquic_moq_version version) { - if(version == IMQUIC_MOQ_VERSION_11) { - switch(type) { - case IMQUIC_MOQ_SUBGROUP_HEADER_NOSGID0_NOEXT_v11: - case IMQUIC_MOQ_SUBGROUP_HEADER_NOSGID0_v11: - case IMQUIC_MOQ_SUBGROUP_HEADER_NOSGID_NOEXT_v11: - case IMQUIC_MOQ_SUBGROUP_HEADER_NOSGID_v11: - case IMQUIC_MOQ_SUBGROUP_HEADER_NOEXT_v11: - case IMQUIC_MOQ_SUBGROUP_HEADER_v11: - return "SUBGROUP_HEADER"; - case IMQUIC_MOQ_FETCH_HEADER: - return "FETCH_HEADER"; - default: break; - } - } else { - if(type == IMQUIC_MOQ_FETCH_HEADER) - return "FETCH_HEADER"; - else if(imquic_moq_is_data_message_type_valid(version, type)) - return "SUBGROUP_HEADER"; - } + if(type == IMQUIC_MOQ_FETCH_HEADER) + return "FETCH_HEADER"; + else if(imquic_moq_is_data_message_type_valid(version, type)) + return "SUBGROUP_HEADER"; return NULL; } imquic_moq_delivery imquic_moq_data_message_type_to_delivery(imquic_moq_data_message_type type, imquic_moq_version version) { - if(version == IMQUIC_MOQ_VERSION_11) { - switch(type) { - case IMQUIC_MOQ_SUBGROUP_HEADER_NOSGID0_NOEXT_v11: - case IMQUIC_MOQ_SUBGROUP_HEADER_NOSGID0_v11: - case IMQUIC_MOQ_SUBGROUP_HEADER_NOSGID_NOEXT_v11: - case IMQUIC_MOQ_SUBGROUP_HEADER_NOSGID_v11: - case IMQUIC_MOQ_SUBGROUP_HEADER_NOEXT_v11: - case IMQUIC_MOQ_SUBGROUP_HEADER_v11: - return IMQUIC_MOQ_USE_SUBGROUP; - case IMQUIC_MOQ_FETCH_HEADER: - return IMQUIC_MOQ_USE_FETCH; - default: break; - } - } else { - if(type == IMQUIC_MOQ_FETCH_HEADER) - return IMQUIC_MOQ_USE_FETCH; - else if((type >= IMQUIC_MOQ_SUBGROUP_HEADER_RANGE1_MIN && type <= IMQUIC_MOQ_SUBGROUP_HEADER_RANGE1_MAX) || - (version >= IMQUIC_MOQ_VERSION_15 && (type >= IMQUIC_MOQ_SUBGROUP_HEADER_RANGE2_MIN && type <= IMQUIC_MOQ_SUBGROUP_HEADER_RANGE2_MAX))) - return IMQUIC_MOQ_USE_SUBGROUP; - } + if(type == IMQUIC_MOQ_FETCH_HEADER) + return IMQUIC_MOQ_USE_FETCH; + else if((type >= IMQUIC_MOQ_SUBGROUP_HEADER_RANGE1_MIN && type <= IMQUIC_MOQ_SUBGROUP_HEADER_RANGE1_MAX) || + (type >= IMQUIC_MOQ_SUBGROUP_HEADER_RANGE2_MIN && type <= IMQUIC_MOQ_SUBGROUP_HEADER_RANGE2_MAX)) + return IMQUIC_MOQ_USE_SUBGROUP; return -1; } gboolean imquic_moq_is_fetch_serialization_flags_valid(imquic_moq_version version, uint64_t flags) { - if(version == IMQUIC_MOQ_VERSION_15) { - return (flags < 0x40); - } else if(version >= IMQUIC_MOQ_VERSION_16) { - if(flags > 128 && flags != (uint64_t)0x8C && flags != (uint64_t)0x10C) - return FALSE; - return TRUE; - } - return FALSE; + if(flags > 128 && flags != (uint64_t)0x8C && flags != (uint64_t)0x10C) + return FALSE; + return TRUE; } uint64_t imquic_moq_generate_fetch_serialization_flags(imquic_moq_version version, - imquic_moq_fetch_subgroup_type subgroup, gboolean oid, gboolean group, gboolean priority, gboolean ext, + imquic_moq_fetch_subgroup_type subgroup, gboolean oid, gboolean group, gboolean priority, gboolean prop, gboolean datagram, gboolean end_ne_range, gboolean end_uk_range) { if(end_ne_range) { /* Ignore everything else */ @@ -984,7 +881,7 @@ uint64_t imquic_moq_generate_fetch_serialization_flags(imquic_moq_version versio flags |= 0x08; if(priority) flags |= 0x10; - if(ext) + if(prop) flags |= 0x20; if(datagram) flags |= 0x40; @@ -992,22 +889,8 @@ uint64_t imquic_moq_generate_fetch_serialization_flags(imquic_moq_version versio } void imquic_moq_parse_fetch_serialization_flags(imquic_moq_version version, uint64_t flags, - imquic_moq_fetch_subgroup_type *subgroup, gboolean *oid, gboolean *group, gboolean *priority, gboolean *ext, + imquic_moq_fetch_subgroup_type *subgroup, gboolean *oid, gboolean *group, gboolean *priority, gboolean *prop, gboolean *datagram, gboolean *end_ne_range, gboolean *end_uk_range, gboolean *violation) { - /* For versions previous than v15, we return some defaults */ - if(version < IMQUIC_MOQ_VERSION_15) { - if(subgroup) - *subgroup = IMQUIC_MOQ_FETCH_SUBGROUP_ID; - if(oid) - *oid = TRUE; - if(group) - *group = TRUE; - if(priority) - *priority = TRUE; - if(ext) - *ext = TRUE; - return; - } /* Make sure the provided flags are valid, or return a protocol violation */ if(!imquic_moq_is_fetch_serialization_flags_valid(version, flags)) { if(*violation) @@ -1040,26 +923,26 @@ void imquic_moq_parse_fetch_serialization_flags(imquic_moq_version version, uint *group = (flags8 & 0x08); if(priority) *priority = (flags8 & 0x10); - if(ext) - *ext = (flags8 & 0x20); + if(prop) + *prop = (flags8 & 0x20); if(datagram) *datagram = (flags8 & 0x40); } -const char *imquic_moq_setup_parameter_type_str(imquic_moq_setup_parameter_type type) { +const char *imquic_moq_setup_option_type_str(imquic_moq_setup_option_type type) { switch(type) { - case IMQUIC_MOQ_SETUP_PARAM_PATH: + case IMQUIC_MOQ_SETUP_OPTION_PATH: return "PATH"; - case IMQUIC_MOQ_SETUP_PARAM_MAX_REQUEST_ID: + case IMQUIC_MOQ_SETUP_OPTION_MAX_REQUEST_ID: return "MAX_REQUEST_ID"; - case IMQUIC_MOQ_SETUP_PARAM_AUTHORIZATION_TOKEN: + case IMQUIC_MOQ_SETUP_OPTION_AUTHORIZATION_TOKEN: return "AUTHORIZATION_TOKEN"; - case IMQUIC_MOQ_SETUP_PARAM_MAX_AUTH_TOKEN_CACHE_SIZE: + case IMQUIC_MOQ_SETUP_OPTION_MAX_AUTH_TOKEN_CACHE_SIZE: return "MAX_AUTH_TOKEN_CACHE_SIZE"; - case IMQUIC_MOQ_SETUP_PARAM_AUTHORITY: + case IMQUIC_MOQ_SETUP_OPTION_AUTHORITY: return "AUTHORITY"; - case IMQUIC_MOQ_SETUP_PARAM_MOQT_IMPLEMENTATION: + case IMQUIC_MOQ_SETUP_OPTION_MOQT_IMPLEMENTATION: return "MOQT_IMPLEMENTATION"; default: break; } @@ -1068,18 +951,12 @@ const char *imquic_moq_setup_parameter_type_str(imquic_moq_setup_parameter_type const char *imquic_moq_request_parameter_type_str(imquic_moq_request_parameter_type type, imquic_moq_version version) { switch(type) { - case IMQUIC_MOQ_REQUEST_PARAM_AUTHORIZATION_TOKEN_v11: - if(version < IMQUIC_MOQ_VERSION_12) - return "AUTHORIZATION_TOKEN"; - break; case IMQUIC_MOQ_REQUEST_PARAM_DELIVERY_TIMEOUT: return "DELIVERY_TIMEOUT"; + case IMQUIC_MOQ_REQUEST_PARAM_RENDEZVOUS_TIMEOUT: + return "RENDEZVOUS_TIMEOUT"; case IMQUIC_MOQ_REQUEST_PARAM_AUTHORIZATION_TOKEN: return "AUTHORIZATION_TOKEN"; - case IMQUIC_MOQ_REQUEST_PARAM_MAX_CACHE_DURATION: - return "MAX_CACHE_DURATION"; - case IMQUIC_MOQ_REQUEST_PARAM_PUBLISHER_PRIORITY: - return "PUBLISHER_PRIORITY"; case IMQUIC_MOQ_REQUEST_PARAM_SUBSCRIBER_PRIORITY: return "SUBSCRIBER_PRIORITY"; case IMQUIC_MOQ_REQUEST_PARAM_GROUP_ORDER: @@ -1092,8 +969,6 @@ const char *imquic_moq_request_parameter_type_str(imquic_moq_request_parameter_t return "LARGEST_OBJECT"; case IMQUIC_MOQ_REQUEST_PARAM_FORWARD: return "FORWARD"; - case IMQUIC_MOQ_REQUEST_PARAM_DYNAMIC_GROUPS: - return "DYNAMIC_GROUPS"; case IMQUIC_MOQ_REQUEST_PARAM_NEW_GROUP_REQUEST: return "NEW_GROUP_REQUEST"; default: break; @@ -1170,67 +1045,82 @@ const char *imquic_moq_auth_token_alias_type_str(imquic_moq_auth_token_alias_typ return NULL; } -/* MoQ parameters */ +/* MoQ options and parameters */ static int imquic_moq_compare_types(const void *a, const void *b) { return GPOINTER_TO_INT(a) - GPOINTER_TO_INT(b); } -size_t imquic_moq_setup_parameters_serialize(imquic_moq_context *moq, - imquic_moq_setup_parameters *parameters, +size_t imquic_moq_setup_options_serialize(imquic_moq_context *moq, + imquic_moq_setup_options *options, uint8_t *bytes, size_t blen, uint8_t *params_num) { *params_num = 0; if(bytes == NULL || blen == 0) return 0; size_t offset = 0; - if(parameters == NULL) { - /* No parameters */ - offset += imquic_write_varint(0, &bytes[offset], blen-offset); + if(options == NULL) { + /* No options */ + if(moq->version <= IMQUIC_MOQ_VERSION_16) + offset += imquic_write_moqint(moq->version, 0, &bytes[offset], blen-offset); } else { uint64_t new_id = 0, last_id = 0; GList *list = NULL; - if(parameters->path_set) - list = g_list_append(list, GUINT_TO_POINTER(IMQUIC_MOQ_SETUP_PARAM_PATH)); - if(parameters->max_request_id_set) - list = g_list_append(list, GUINT_TO_POINTER(IMQUIC_MOQ_SETUP_PARAM_MAX_REQUEST_ID)); - if(parameters->max_auth_token_cache_size_set) - list = g_list_append(list, GUINT_TO_POINTER(IMQUIC_MOQ_SETUP_PARAM_MAX_AUTH_TOKEN_CACHE_SIZE)); - if(moq->version >= IMQUIC_MOQ_VERSION_12 && parameters->auth_token_set) - list = g_list_append(list, GUINT_TO_POINTER(IMQUIC_MOQ_SETUP_PARAM_AUTHORIZATION_TOKEN)); - if(moq->version >= IMQUIC_MOQ_VERSION_14 && parameters->authority_set) - list = g_list_append(list, GUINT_TO_POINTER(IMQUIC_MOQ_SETUP_PARAM_AUTHORITY)); - if(moq->version >= IMQUIC_MOQ_VERSION_14 && parameters->moqt_implementation_set) - list = g_list_append(list, GUINT_TO_POINTER(IMQUIC_MOQ_SETUP_PARAM_MOQT_IMPLEMENTATION)); + if(options->path_set) + list = g_list_append(list, GUINT_TO_POINTER(IMQUIC_MOQ_SETUP_OPTION_PATH)); + if(options->max_request_id_set) + list = g_list_append(list, GUINT_TO_POINTER(IMQUIC_MOQ_SETUP_OPTION_MAX_REQUEST_ID)); + if(options->max_auth_token_cache_size_set) + list = g_list_append(list, GUINT_TO_POINTER(IMQUIC_MOQ_SETUP_OPTION_MAX_AUTH_TOKEN_CACHE_SIZE)); + if(options->auth_token_set) + list = g_list_append(list, GUINT_TO_POINTER(IMQUIC_MOQ_SETUP_OPTION_AUTHORIZATION_TOKEN)); + if(options->authority_set) + list = g_list_append(list, GUINT_TO_POINTER(IMQUIC_MOQ_SETUP_OPTION_AUTHORITY)); + if(options->moqt_implementation_set) + list = g_list_append(list, GUINT_TO_POINTER(IMQUIC_MOQ_SETUP_OPTION_MOQT_IMPLEMENTATION)); + /* For newer versions, we always add a GREASE option */ + if(moq->version >= IMQUIC_MOQ_VERSION_17) + list = g_list_append(list, GUINT_TO_POINTER(imquic_moq_random_grease())); *params_num = g_list_length(list); - offset += imquic_write_varint(*params_num, &bytes[offset], blen-offset); + if(moq->version <= IMQUIC_MOQ_VERSION_16) + offset += imquic_write_moqint(moq->version, *params_num, &bytes[offset], blen-offset); if(list != NULL) { - if(moq->version >= IMQUIC_MOQ_VERSION_16) - list = g_list_sort(list, imquic_moq_compare_types); + list = g_list_sort(list, imquic_moq_compare_types); GList *temp = list; while(temp) { new_id = GPOINTER_TO_UINT(temp->data); - if(new_id == IMQUIC_MOQ_SETUP_PARAM_PATH) { - offset += imquic_moq_parameter_add_data(moq, &bytes[offset], blen-offset, + if(new_id == IMQUIC_MOQ_SETUP_OPTION_PATH) { + offset += imquic_moq_setup_option_add_data(moq, &bytes[offset], blen-offset, new_id, last_id, - (uint8_t *)parameters->path, strlen(parameters->path)); - } else if(new_id == IMQUIC_MOQ_SETUP_PARAM_MAX_REQUEST_ID) { - offset += imquic_moq_parameter_add_int(moq, &bytes[offset], blen-offset, + (uint8_t *)options->path, strlen(options->path)); + } else if(new_id == IMQUIC_MOQ_SETUP_OPTION_MAX_REQUEST_ID) { + offset += imquic_moq_setup_option_add_int(moq, &bytes[offset], blen-offset, new_id, last_id, - parameters->max_request_id); - } else if(new_id == IMQUIC_MOQ_SETUP_PARAM_MAX_AUTH_TOKEN_CACHE_SIZE) { - offset += imquic_moq_parameter_add_int(moq, &bytes[offset], blen-offset, + options->max_request_id); + } else if(new_id == IMQUIC_MOQ_SETUP_OPTION_MAX_AUTH_TOKEN_CACHE_SIZE) { + offset += imquic_moq_setup_option_add_int(moq, &bytes[offset], blen-offset, new_id, last_id, - parameters->max_auth_token_cache_size); - } else if(new_id == IMQUIC_MOQ_SETUP_PARAM_AUTHORIZATION_TOKEN) { - offset += imquic_moq_parameter_add_data(moq, &bytes[offset], blen-offset, + options->max_auth_token_cache_size); + } else if(new_id == IMQUIC_MOQ_SETUP_OPTION_AUTHORIZATION_TOKEN) { + offset += imquic_moq_setup_option_add_data(moq, &bytes[offset], blen-offset, new_id, last_id, - parameters->auth_token, parameters->auth_token_len); - } else if(new_id == IMQUIC_MOQ_SETUP_PARAM_AUTHORITY) { - offset += imquic_moq_parameter_add_data(moq, &bytes[offset], blen-offset, + options->auth_token, options->auth_token_len); + } else if(new_id == IMQUIC_MOQ_SETUP_OPTION_AUTHORITY) { + offset += imquic_moq_setup_option_add_data(moq, &bytes[offset], blen-offset, new_id, last_id, - (uint8_t *)parameters->authority, strlen(parameters->authority)); - } else if(new_id == IMQUIC_MOQ_SETUP_PARAM_MOQT_IMPLEMENTATION) { - offset += imquic_moq_parameter_add_data(moq, &bytes[offset], blen-offset, + (uint8_t *)options->authority, strlen(options->authority)); + } else if(new_id == IMQUIC_MOQ_SETUP_OPTION_MOQT_IMPLEMENTATION) { + offset += imquic_moq_setup_option_add_data(moq, &bytes[offset], blen-offset, new_id, last_id, - (uint8_t *)parameters->moqt_implementation, strlen(parameters->moqt_implementation)); + (uint8_t *)options->moqt_implementation, strlen(options->moqt_implementation)); + } else if(moq->version >= IMQUIC_MOQ_VERSION_17 && imquic_moq_is_grease(new_id)) { + /* Add a GREASE setup option */ + if(new_id % 2 == 0) { + uint64_t value = g_random_int_range(1, 1000); + offset += imquic_moq_setup_option_add_int(moq, &bytes[offset], blen-offset, + new_id, last_id, value); + } else { + uint8_t data[] = { 0x01, 0x02, 0x03, 0x04 }; + offset += imquic_moq_setup_option_add_data(moq, &bytes[offset], blen-offset, + new_id, last_id, data, sizeof(data)); + } } last_id = new_id; temp = temp->next; @@ -1245,117 +1135,123 @@ void imquic_moq_request_parameters_init_defaults(imquic_moq_request_parameters * if(parameters == NULL) return; memset(parameters, 0, sizeof(imquic_moq_request_parameters)); - parameters->publisher_priority = 128; parameters->subscriber_priority = 128; parameters->group_order = IMQUIC_MOQ_ORDERING_ASCENDING; parameters->forward = TRUE; } size_t imquic_moq_request_parameters_serialize(imquic_moq_context *moq, - imquic_moq_request_parameters *parameters, + imquic_moq_message_type request, imquic_moq_request_parameters *parameters, uint8_t *bytes, size_t blen, uint8_t *params_num) { if(bytes == NULL || blen == 0) return 0; size_t offset = 0; if(parameters == NULL) { /* No parameters */ - offset += imquic_write_varint(0, &bytes[offset], blen-offset); + offset += imquic_write_moqint(moq->version, 0, &bytes[offset], blen-offset); } else { uint64_t new_id = 0, last_id = 0; GList *list = NULL; - if(parameters->auth_token_set) - list = g_list_append(list, GUINT_TO_POINTER((moq->version >= IMQUIC_MOQ_VERSION_12 ? - IMQUIC_MOQ_REQUEST_PARAM_AUTHORIZATION_TOKEN : IMQUIC_MOQ_REQUEST_PARAM_AUTHORIZATION_TOKEN_v11))); - if(parameters->delivery_timeout_set && parameters->delivery_timeout > 0) + if(parameters->auth_token_set && request != IMQUIC_MOQ_REQUEST_OK && request != IMQUIC_MOQ_REQUEST_ERROR) { + list = g_list_append(list, GUINT_TO_POINTER(IMQUIC_MOQ_REQUEST_PARAM_AUTHORIZATION_TOKEN)); + } + if(parameters->delivery_timeout_set && parameters->delivery_timeout > 0 && + (request == IMQUIC_MOQ_PUBLISH_OK || request == IMQUIC_MOQ_SUBSCRIBE || request == IMQUIC_MOQ_REQUEST_UPDATE)) { list = g_list_append(list, GUINT_TO_POINTER(IMQUIC_MOQ_REQUEST_PARAM_DELIVERY_TIMEOUT)); - if(parameters->max_cache_duration_set && moq->version <= IMQUIC_MOQ_VERSION_15) - list = g_list_append(list, GUINT_TO_POINTER(IMQUIC_MOQ_REQUEST_PARAM_MAX_CACHE_DURATION)); - if(moq->version == IMQUIC_MOQ_VERSION_15 && parameters->publisher_priority_set) - list = g_list_append(list, GUINT_TO_POINTER(IMQUIC_MOQ_REQUEST_PARAM_PUBLISHER_PRIORITY)); - if(moq->version >= IMQUIC_MOQ_VERSION_15 && parameters->subscriber_priority_set) + } + if(parameters->rendezvous_timeout_set && parameters->rendezvous_timeout > 0 && + moq->version >= IMQUIC_MOQ_VERSION_17 && request == IMQUIC_MOQ_SUBSCRIBE) { + list = g_list_append(list, GUINT_TO_POINTER(IMQUIC_MOQ_REQUEST_PARAM_RENDEZVOUS_TIMEOUT)); + } + if(parameters->subscriber_priority_set && + (request == IMQUIC_MOQ_PUBLISH_OK || request == IMQUIC_MOQ_SUBSCRIBE || request == IMQUIC_MOQ_FETCH || request == IMQUIC_MOQ_REQUEST_UPDATE)) { list = g_list_append(list, GUINT_TO_POINTER(IMQUIC_MOQ_REQUEST_PARAM_SUBSCRIBER_PRIORITY)); - if(moq->version >= IMQUIC_MOQ_VERSION_15 && parameters->group_order_set) + } + if(parameters->group_order_set && + (request == IMQUIC_MOQ_PUBLISH_OK || request == IMQUIC_MOQ_SUBSCRIBE || request == IMQUIC_MOQ_FETCH)) { list = g_list_append(list, GUINT_TO_POINTER(IMQUIC_MOQ_REQUEST_PARAM_GROUP_ORDER)); - if(moq->version >= IMQUIC_MOQ_VERSION_15 && parameters->subscription_filter_set) + } + if(parameters->subscription_filter_set && + (request == IMQUIC_MOQ_PUBLISH_OK || request == IMQUIC_MOQ_SUBSCRIBE || request == IMQUIC_MOQ_REQUEST_UPDATE)) { list = g_list_append(list, GUINT_TO_POINTER(IMQUIC_MOQ_REQUEST_PARAM_SUBSCRIPTION_FILTER)); - if(moq->version >= IMQUIC_MOQ_VERSION_15 && parameters->expires_set) + } + if(parameters->expires_set && + (request == IMQUIC_MOQ_PUBLISH || request == IMQUIC_MOQ_PUBLISH_OK || request == IMQUIC_MOQ_REQUEST_OK)) { list = g_list_append(list, GUINT_TO_POINTER(IMQUIC_MOQ_REQUEST_PARAM_EXPIRES)); - if(moq->version >= IMQUIC_MOQ_VERSION_15 && parameters->largest_object_set) + } + if(parameters->largest_object_set && + (request == IMQUIC_MOQ_PUBLISH || request == IMQUIC_MOQ_SUBSCRIBE_OK || request == IMQUIC_MOQ_REQUEST_OK)) { list = g_list_append(list, GUINT_TO_POINTER(IMQUIC_MOQ_REQUEST_PARAM_LARGEST_OBJECT)); - if(moq->version >= IMQUIC_MOQ_VERSION_15 && parameters->forward_set) + } + if(parameters->forward_set && + (request == IMQUIC_MOQ_PUBLISH || request == IMQUIC_MOQ_PUBLISH_OK || request == IMQUIC_MOQ_SUBSCRIBE || + request == IMQUIC_MOQ_REQUEST_UPDATE || request == IMQUIC_MOQ_SUBSCRIBE_NAMESPACE)) { list = g_list_append(list, GUINT_TO_POINTER(IMQUIC_MOQ_REQUEST_PARAM_FORWARD)); - if(moq->version == IMQUIC_MOQ_VERSION_15 && parameters->dynamic_groups_set) - list = g_list_append(list, GUINT_TO_POINTER(IMQUIC_MOQ_REQUEST_PARAM_DYNAMIC_GROUPS)); - if(moq->version >= IMQUIC_MOQ_VERSION_15 && parameters->new_group_request_set) + } + if(parameters->new_group_request_set && + (request == IMQUIC_MOQ_PUBLISH_OK || request == IMQUIC_MOQ_SUBSCRIBE || request == IMQUIC_MOQ_REQUEST_UPDATE)) { list = g_list_append(list, GUINT_TO_POINTER(IMQUIC_MOQ_REQUEST_PARAM_NEW_GROUP_REQUEST)); + } *params_num = g_list_length(list); - offset += imquic_write_varint(*params_num, &bytes[offset], blen-offset); + offset += imquic_write_moqint(moq->version, *params_num, &bytes[offset], blen-offset); if(list != NULL) { - if(moq->version >= IMQUIC_MOQ_VERSION_16) - list = g_list_sort(list, imquic_moq_compare_types); + list = g_list_sort(list, imquic_moq_compare_types); GList *temp = list; while(temp) { new_id = GPOINTER_TO_UINT(temp->data); - if(new_id == IMQUIC_MOQ_REQUEST_PARAM_AUTHORIZATION_TOKEN || new_id == IMQUIC_MOQ_REQUEST_PARAM_AUTHORIZATION_TOKEN_v11) { + if(new_id == IMQUIC_MOQ_REQUEST_PARAM_AUTHORIZATION_TOKEN) { offset += imquic_moq_parameter_add_data(moq, &bytes[offset], blen-offset, new_id, last_id, parameters->auth_token, parameters->auth_token_len); } else if(new_id == IMQUIC_MOQ_REQUEST_PARAM_DELIVERY_TIMEOUT) { - offset += imquic_moq_parameter_add_int(moq, &bytes[offset], blen-offset, + offset += imquic_moq_parameter_add_varint(moq, &bytes[offset], blen-offset, new_id, last_id, parameters->delivery_timeout); - } else if(new_id == IMQUIC_MOQ_REQUEST_PARAM_MAX_CACHE_DURATION) { - offset += imquic_moq_parameter_add_int(moq, &bytes[offset], blen-offset, - new_id, last_id, - parameters->max_cache_duration); - } else if(new_id == IMQUIC_MOQ_REQUEST_PARAM_PUBLISHER_PRIORITY) { - offset += imquic_moq_parameter_add_int(moq, &bytes[offset], blen-offset, + } else if(new_id == IMQUIC_MOQ_REQUEST_PARAM_RENDEZVOUS_TIMEOUT) { + offset += imquic_moq_parameter_add_varint(moq, &bytes[offset], blen-offset, new_id, last_id, - (uint64_t)parameters->publisher_priority); + parameters->rendezvous_timeout); } else if(new_id == IMQUIC_MOQ_REQUEST_PARAM_SUBSCRIBER_PRIORITY) { - offset += imquic_moq_parameter_add_int(moq, &bytes[offset], blen-offset, + offset += imquic_moq_parameter_add_uint8(moq, &bytes[offset], blen-offset, new_id, last_id, (uint64_t)parameters->subscriber_priority); } else if(new_id == IMQUIC_MOQ_REQUEST_PARAM_GROUP_ORDER) { - offset += imquic_moq_parameter_add_int(moq, &bytes[offset], blen-offset, + offset += imquic_moq_parameter_add_uint8(moq, &bytes[offset], blen-offset, new_id, last_id, parameters->group_order); } else if(new_id == IMQUIC_MOQ_REQUEST_PARAM_SUBSCRIPTION_FILTER) { uint8_t temp[40]; size_t tlen = sizeof(temp); - size_t toffset = imquic_write_varint(parameters->subscription_filter.type, temp, tlen); + size_t toffset = imquic_write_moqint(moq->version, parameters->subscription_filter.type, temp, tlen); if(parameters->subscription_filter.type == IMQUIC_MOQ_FILTER_ABSOLUTE_START || parameters->subscription_filter.type == IMQUIC_MOQ_FILTER_ABSOLUTE_RANGE) { - toffset += imquic_write_varint(parameters->subscription_filter.start_location.group, &temp[toffset], tlen-toffset); - toffset += imquic_write_varint(parameters->subscription_filter.start_location.object, &temp[toffset], tlen-toffset); + toffset += imquic_write_moqint(moq->version, parameters->subscription_filter.start_location.group, &temp[toffset], tlen-toffset); + toffset += imquic_write_moqint(moq->version, parameters->subscription_filter.start_location.object, &temp[toffset], tlen-toffset); + } + if(parameters->subscription_filter.type == IMQUIC_MOQ_FILTER_ABSOLUTE_RANGE) { + /* End group is a delta, starting from v17 */ + uint64_t end_group = parameters->subscription_filter.end_group; + if(moq->version >= IMQUIC_MOQ_VERSION_16) + end_group -= parameters->subscription_filter.start_location.group; + toffset += imquic_write_moqint(moq->version, end_group, &temp[toffset], tlen-toffset); } - if(parameters->subscription_filter.type == IMQUIC_MOQ_FILTER_ABSOLUTE_RANGE) - toffset += imquic_write_varint(parameters->subscription_filter.end_group, &temp[toffset], tlen-toffset); offset += imquic_moq_parameter_add_data(moq, &bytes[offset], blen-offset, new_id, last_id, temp, toffset); } else if(new_id == IMQUIC_MOQ_REQUEST_PARAM_EXPIRES) { - offset += imquic_moq_parameter_add_int(moq, &bytes[offset], blen-offset, + offset += imquic_moq_parameter_add_varint(moq, &bytes[offset], blen-offset, new_id, last_id, parameters->expires); } else if(new_id == IMQUIC_MOQ_REQUEST_PARAM_LARGEST_OBJECT) { - uint8_t temp[40]; - size_t tlen = sizeof(temp); - size_t toffset = imquic_write_varint(parameters->largest_object.group, temp, tlen); - toffset += imquic_write_varint(parameters->largest_object.object, &temp[toffset], tlen-toffset); - offset += imquic_moq_parameter_add_data(moq, &bytes[offset], blen-offset, + offset += imquic_moq_parameter_add_location(moq, &bytes[offset], blen-offset, new_id, last_id, - temp, toffset); + ¶meters->largest_object); } else if(new_id == IMQUIC_MOQ_REQUEST_PARAM_FORWARD) { - offset += imquic_moq_parameter_add_int(moq, &bytes[offset], blen-offset, + offset += imquic_moq_parameter_add_uint8(moq, &bytes[offset], blen-offset, new_id, last_id, (uint64_t)parameters->forward); - } else if(new_id == IMQUIC_MOQ_REQUEST_PARAM_DYNAMIC_GROUPS) { - offset += imquic_moq_parameter_add_int(moq, &bytes[offset], blen-offset, - new_id, last_id, - (uint64_t)parameters->dynamic_groups); } else if(new_id == IMQUIC_MOQ_REQUEST_PARAM_NEW_GROUP_REQUEST) { - offset += imquic_moq_parameter_add_int(moq, &bytes[offset], blen-offset, + offset += imquic_moq_parameter_add_varint(moq, &bytes[offset], blen-offset, new_id, last_id, (uint64_t)parameters->new_group_request); } @@ -1387,12 +1283,22 @@ void imquic_moq_subscription_destroy(imquic_moq_subscription *moq_sub) { } } +static void imquic_moq_stream_free(const imquic_refcount *ms_ref) { + imquic_moq_stream *moq_stream = imquic_refcount_containerof(ms_ref, imquic_moq_stream, ref); + imquic_moq_namespace_free(moq_stream->namespace_prefix); + imquic_buffer_destroy(moq_stream->buffer); + g_free(moq_stream); +} + +imquic_moq_stream *imquic_moq_stream_create(void) { + imquic_moq_stream *moq_stream = g_malloc0(sizeof(imquic_moq_stream)); + imquic_refcount_init(&moq_stream->ref, imquic_moq_stream_free); + return moq_stream; +} + void imquic_moq_stream_destroy(imquic_moq_stream *moq_stream) { - if(moq_stream != NULL) { - imquic_moq_namespace_free(moq_stream->namespace_prefix); - imquic_buffer_destroy(moq_stream->buffer); - g_free(moq_stream); - } + if(moq_stream && g_atomic_int_compare_and_exchange(&moq_stream->destroyed, 0, 1)) + imquic_refcount_decrease(&moq_stream->ref); } /* Parsing and building macros */ @@ -1406,15 +1312,15 @@ void imquic_moq_stream_destroy(imquic_moq_stream *moq_stream) { #define IMQUIC_MOQ_PARSE_NAMESPACES(request, tns_num, i, error_message, last) \ do { \ - tns_num = imquic_read_varint(&bytes[offset], blen-offset, &length); \ + tns_num = imquic_read_moqint(moq->version, &bytes[offset], blen-offset, &length); \ IMQUIC_MOQ_CHECK_ERR(length == 0 || (tns_num > 0 && length >= blen-offset), NULL, 0, 0, error_message); \ - IMQUIC_MOQ_CHECK_ERR((tns_num == 0 && request != IMQUIC_MOQ_NAMESPACE && request != IMQUIC_MOQ_NAMESPACE_DONE) || tns_num > 32, error, IMQUIC_MOQ_PROTOCOL_VIOLATION, 0, "Invalid number of namespaces"); \ + IMQUIC_MOQ_CHECK_ERR((tns_num == 0 && request != IMQUIC_MOQ_SUBSCRIBE_NAMESPACE && request != IMQUIC_MOQ_NAMESPACE && request != IMQUIC_MOQ_NAMESPACE_DONE) || tns_num > 32, error, IMQUIC_MOQ_PROTOCOL_VIOLATION, 0, "Invalid number of namespaces"); \ offset += length; \ uint64_t total_len = 0; \ i = 0; \ for(i = 0; i < tns_num; i++) { \ IMQUIC_MOQ_CHECK_ERR(blen-offset == 0, NULL, 0, 0, error_message); \ - uint64_t tns_len = imquic_read_varint(&bytes[offset], blen-offset, &length); \ + uint64_t tns_len = imquic_read_moqint(moq->version, &bytes[offset], blen-offset, &length); \ IMQUIC_MOQ_CHECK_ERR(length == 0 || length >= blen-offset, NULL, 0, 0, error_message); \ IMQUIC_MOQ_CHECK_ERR(tns_len == 0, error, IMQUIC_MOQ_PROTOCOL_VIOLATION, 0, "Invalid track namespace field length"); \ offset += length; \ @@ -1438,7 +1344,7 @@ void imquic_moq_stream_destroy(imquic_moq_stream *moq_stream) { #define IMQUIC_MOQ_PARSE_TRACKNAME(error_message, last) \ do { \ - uint64_t tn_len = imquic_read_varint(&bytes[offset], blen-offset, &length); \ + uint64_t tn_len = imquic_read_moqint(moq->version, &bytes[offset], blen-offset, &length); \ if(last) { \ IMQUIC_MOQ_CHECK_ERR(length == 0 || length > blen-offset, NULL, 0, 0, error_message); \ } else { \ @@ -1469,15 +1375,15 @@ void imquic_moq_stream_destroy(imquic_moq_stream *moq_stream) { tns_num++; \ temp = temp->next; \ } \ - if((tns_num == 0 && request != IMQUIC_MOQ_NAMESPACE && request != IMQUIC_MOQ_NAMESPACE_DONE) || tns_num > 32) { \ + if((tns_num == 0 && request != IMQUIC_MOQ_SUBSCRIBE_NAMESPACE && request != IMQUIC_MOQ_NAMESPACE && request != IMQUIC_MOQ_NAMESPACE_DONE) || tns_num > 32) { \ IMQUIC_LOG(IMQUIC_LOG_ERR, "[%s][MoQ] Can't add MoQ %s: invalid number of tuples\n", \ imquic_get_connection_name(moq->conn), imquic_moq_message_type_str(request, moq->version)); \ return 0; \ } \ - offset += imquic_write_varint(tns_num, &bytes[offset], blen-offset); \ + offset += imquic_write_moqint(moq->version, tns_num, &bytes[offset], blen-offset); \ temp = track_namespace; \ while(temp) { \ - offset += imquic_write_varint(temp->length, &bytes[offset], blen-offset); \ + offset += imquic_write_moqint(moq->version, temp->length, &bytes[offset], blen-offset); \ if(temp->length > 0) { \ memcpy(&bytes[offset], temp->buffer, temp->length); \ offset += temp->length; \ @@ -1493,7 +1399,7 @@ void imquic_moq_stream_destroy(imquic_moq_stream *moq_stream) { imquic_get_connection_name(moq->conn), imquic_moq_message_type_str(request, moq->version)); \ return 0; \ } \ - offset += imquic_write_varint(track_name->length, &bytes[offset], blen-offset); \ + offset += imquic_write_moqint(moq->version, track_name->length, &bytes[offset], blen-offset); \ if(track_name->length > 0) { \ memcpy(&bytes[offset], track_name->buffer, track_name->length); \ offset += track_name->length; \ @@ -1502,7 +1408,7 @@ void imquic_moq_stream_destroy(imquic_moq_stream *moq_stream) { #define IMQUIC_MOQ_ADD_MESSAGE_TYPE(type) \ do { \ - offset = imquic_write_varint(type, bytes, blen); \ + offset = imquic_write_moqint(moq->version, type, bytes, blen); \ len_offset = offset; \ offset += 2; \ } while(0) @@ -1515,12 +1421,21 @@ void imquic_moq_stream_destroy(imquic_moq_stream *moq_stream) { } while(0) /* Parse MoQ messages */ +static uint64_t imquic_moq_get_control_stream(imquic_moq_context *moq) { + return (moq->version <= IMQUIC_MOQ_VERSION_16) ? moq->control_stream_id : moq->remote_control_stream_id; +} +static gboolean imquic_moq_is_control_stream(imquic_moq_context *moq, uint64_t stream_id) { + if((moq->version <= IMQUIC_MOQ_VERSION_16 && stream_id == moq->control_stream_id) || + (moq->version >= IMQUIC_MOQ_VERSION_17 && stream_id == moq->remote_control_stream_id)) + return TRUE; + return FALSE; +} int imquic_moq_parse_message(imquic_moq_context *moq, uint64_t stream_id, uint8_t *bytes, size_t blen, gboolean complete, gboolean datagram) { size_t offset = 0, parsed = 0, parsed_prev = 0; uint8_t tlen = 0, error = 0; /* If this is a datagram, it can only be OBJECT_DATAGRAM or OBJECT_DATAGRAM_STATUS */ if(datagram) { - imquic_moq_datagram_message_type dtype = imquic_read_varint(&bytes[offset], blen-offset, &tlen); + imquic_moq_datagram_message_type dtype = imquic_read_moqint(moq->version, &bytes[offset], blen-offset, &tlen); offset += tlen; gboolean valid = FALSE, payload = FALSE, violation = FALSE; valid = imquic_moq_is_datagram_message_type_valid(moq->version, dtype); @@ -1544,51 +1459,65 @@ int imquic_moq_parse_message(imquic_moq_context *moq, uint64_t stream_id, uint8_ return 0; } /* Check if this is a media stream */ + imquic_mutex_lock(&moq->mutex); imquic_moq_stream *moq_stream = g_hash_table_lookup(moq->streams, &stream_id); - if(stream_id == moq->control_stream_id) { + if(imquic_moq_is_control_stream(moq, stream_id)) { imquic_buffer_append(moq->buffer, bytes, blen); bytes = moq->buffer->bytes; blen = moq->buffer->length; - } else if(moq_stream != NULL && moq_stream->subscribe_namespace) { + } else if(moq_stream != NULL && moq_stream->request_type > 0) { if(moq_stream->buffer == NULL) moq_stream->buffer = imquic_buffer_create(NULL, 0); imquic_buffer_append(moq_stream->buffer, bytes, blen); bytes = moq_stream->buffer->bytes; blen = moq_stream->buffer->length; } + if(moq_stream != NULL) + imquic_refcount_increase(&moq_stream->ref); + imquic_mutex_unlock(&moq->mutex); /* Iterate on all frames */ - while((moq_stream == NULL || moq_stream->subscribe_namespace) && blen-offset > 0) { - /* If we're here, we're either on the control stream, or on a media stream waiting to know what it will be like */ - imquic_moq_message_type type = imquic_read_varint(&bytes[offset], blen-offset, &tlen); + while((moq_stream == NULL || moq_stream->request_type != 0) && blen-offset > 0) { + /* If we're here, we're either on the control stream, on a request + * stream, or on a media stream waiting to know what it will be like */ + imquic_moq_message_type type = imquic_read_moqint(moq->version, &bytes[offset], blen-offset, &tlen); IMQUIC_LOG(IMQUIC_MOQ_LOG_VERB, "[%s][MoQ][%zu] >> %s (%02x, %u)\n", imquic_get_connection_name(moq->conn), offset, imquic_moq_message_type_str(type, moq->version), type, tlen); - if(stream_id != moq->control_stream_id && moq_stream == NULL) { - /* Not the control stream, check what it's for (namespaces + if(!imquic_moq_is_control_stream(moq, stream_id) && moq_stream == NULL) { + /* Not the control stream, check what it's for (request * or objects) and then make sure it's a supported message */ gboolean bidirectional = FALSE; imquic_parse_stream_id(stream_id, NULL, NULL, &bidirectional); imquic_moq_data_message_type dtype = (imquic_moq_data_message_type)type; - if(bidirectional && type == IMQUIC_MOQ_SUBSCRIBE_NAMESPACE && moq->version >= IMQUIC_MOQ_VERSION_16) { - /* Create a new MoQ stream for namespaces and track it */ - IMQUIC_LOG(IMQUIC_MOQ_LOG_HUGE, "[%s][MoQ] -- Stream %"SCNu64" will be used for namespaces\n", - imquic_get_connection_name(moq->conn), stream_id); - moq_stream = g_malloc0(sizeof(imquic_moq_stream)); + if(bidirectional && (type == IMQUIC_MOQ_PUBLISH_NAMESPACE || type == IMQUIC_MOQ_SUBSCRIBE_NAMESPACE || + type == IMQUIC_MOQ_PUBLISH || type == IMQUIC_MOQ_SUBSCRIBE || + type == IMQUIC_MOQ_FETCH || type == IMQUIC_MOQ_TRACK_STATUS)) { + /* Create a new MoQ stream for the request and track it */ + IMQUIC_LOG(IMQUIC_MOQ_LOG_HUGE, "[%s][MoQ] -- Stream %"SCNu64" will be used for %s\n", + imquic_get_connection_name(moq->conn), stream_id, imquic_moq_message_type_str(type, moq->version)); + moq_stream = imquic_moq_stream_create(); moq_stream->stream_id = stream_id; - moq_stream->subscribe_namespace = TRUE; - moq_stream->namespace_publisher = TRUE; - g_hash_table_insert(moq->streams, imquic_dup_uint64(stream_id), moq_stream); + moq_stream->request_type = type; moq_stream->buffer = imquic_buffer_create(bytes, blen); bytes = moq_stream->buffer->bytes; blen = moq_stream->buffer->length; + imquic_mutex_lock(&moq->mutex); + g_hash_table_insert(moq->streams, imquic_dup_uint64(stream_id), moq_stream); + imquic_mutex_unlock(&moq->mutex); + /* This reference is for managing the message */ + imquic_refcount_increase(&moq_stream->ref); } else if(!bidirectional && imquic_moq_is_data_message_type_valid(moq->version, dtype)) { /* Create a new MoQ stream for data and track it */ IMQUIC_LOG(IMQUIC_MOQ_LOG_HUGE, "[%s][MoQ] -- Stream %"SCNu64" will be used for %s\n", imquic_get_connection_name(moq->conn), stream_id, imquic_moq_data_message_type_str(dtype, moq->version)); - moq_stream = g_malloc0(sizeof(imquic_moq_stream)); + moq_stream = imquic_moq_stream_create(); moq_stream->stream_id = stream_id; moq_stream->type = dtype; moq_stream->priority = 128; /* FIXME */ + imquic_mutex_lock(&moq->mutex); g_hash_table_insert(moq->streams, imquic_dup_uint64(stream_id), moq_stream); + imquic_mutex_unlock(&moq->mutex); + /* This reference is for managing the message */ + imquic_refcount_increase(&moq_stream->ref); } else { /* TODO Handle failure */ IMQUIC_LOG(IMQUIC_LOG_ERR, "[%s][MoQ] MoQ message '%s' (%02x) is not allowed on media streams\n", @@ -1598,57 +1527,71 @@ int imquic_moq_parse_message(imquic_moq_context *moq, uint64_t stream_id, uint8_ } parsed_prev = parsed; offset += tlen; - if(stream_id == moq->control_stream_id) { + if(imquic_moq_is_control_stream(moq, stream_id)) { /* Control message */ size_t plen = blen-offset; - if((moq->version >= IMQUIC_MOQ_VERSION_MIN && moq->version <= IMQUIC_MOQ_VERSION_MAX) || - moq->version == IMQUIC_MOQ_VERSION_ANY || moq->version == IMQUIC_MOQ_VERSION_ANY_LEGACY) { - /* Versions 11 and beyond require a 16 bit integer */ - tlen = 2; - if(blen - offset < tlen) { - /* Try again later */ - IMQUIC_LOG(IMQUIC_MOQ_LOG_VERB, "[%s][MoQ] Not enough bytes available to get the length of the control message (%"SCNu8" > %zu), waiting for more data\n", - imquic_get_connection_name(moq->conn), tlen, blen-offset); - goto done; - } - uint16_t clen = 0; - memcpy(&clen, &bytes[offset], tlen); - plen = ntohs(clen); - offset += tlen; - if(plen > blen-offset) { - /* Try again later */ - IMQUIC_LOG(IMQUIC_MOQ_LOG_VERB, "[%s][MoQ] Not enough bytes available to parse this message (%zu > %zu), waiting for more data\n", - imquic_get_connection_name(moq->conn), plen, blen-offset); - goto done; - } + tlen = 2; + if(blen - offset < tlen) { + /* Try again later */ + IMQUIC_LOG(IMQUIC_MOQ_LOG_VERB, "[%s][MoQ] Not enough bytes available to get the length of the control message (%"SCNu8" > %zu), waiting for more data\n", + imquic_get_connection_name(moq->conn), tlen, blen-offset); + goto done; } - if(type == IMQUIC_MOQ_CLIENT_SETUP) { - /* Parse this CLIENT_SETUP message */ - parsed = imquic_moq_parse_client_setup(moq, &bytes[offset], plen, &error); - } else if(type == IMQUIC_MOQ_SERVER_SETUP) { - /* Parse this SERVER_SETUP message */ - parsed = imquic_moq_parse_server_setup(moq, &bytes[offset], plen, &error); - } else if(type == IMQUIC_MOQ_MAX_REQUEST_ID) { - /* Parse this MAX_REQUEST_ID message */ - parsed = imquic_moq_parse_max_request_id(moq, &bytes[offset], plen, &error); - } else if(type == IMQUIC_MOQ_REQUESTS_BLOCKED) { - /* Parse this REQUESTS_BLOCKED message */ + uint16_t clen = 0; + memcpy(&clen, &bytes[offset], tlen); + plen = ntohs(clen); + offset += tlen; + if(plen > blen-offset) { + /* Try again later */ + IMQUIC_LOG(IMQUIC_MOQ_LOG_VERB, "[%s][MoQ] Not enough bytes available to parse this message (%zu > %zu), waiting for more data\n", + imquic_get_connection_name(moq->conn), plen, blen-offset); + goto done; + } + if(moq->version >= IMQUIC_MOQ_VERSION_17) { + /* On newer versions of the protocol, the unidirectional + * control streams can only carry a limited set of messages */ + if(type == IMQUIC_MOQ_SETUP) { + /* Parse this SETUP message */ + parsed = imquic_moq_parse_setup(moq, &bytes[offset], plen, &error); + } else if(type == IMQUIC_MOQ_GOAWAY) { + /* Parse this GOAWAY message */ + parsed = imquic_moq_parse_goaway(moq, &bytes[offset], plen, &error); + } else { + IMQUIC_LOG(IMQUIC_LOG_ERR, "[%s][MoQ] Unsupported message '%02x' (%s)\n", + imquic_get_connection_name(moq->conn), type, + imquic_moq_message_type_str(type, moq->version)); + error = IMQUIC_MOQ_PROTOCOL_VIOLATION; +#ifdef HAVE_QLOG + if(moq->conn->qlog != NULL && moq->conn->qlog->moq) { + json_t *message = imquic_qlog_moq_message_prepare("unknown"); + imquic_moq_qlog_control_message_parsed(moq->conn->qlog, stream_id, &bytes[offset], plen, message); + } +#endif + } + goto next; + } + /* If we're here, we're on the legacy version of the protocol */ + if(type == IMQUIC_MOQ_CLIENT_SETUP) { + /* Parse this CLIENT_SETUP message */ + parsed = imquic_moq_parse_client_setup(moq, &bytes[offset], plen, &error); + } else if(type == IMQUIC_MOQ_SERVER_SETUP) { + /* Parse this SERVER_SETUP message */ + parsed = imquic_moq_parse_server_setup(moq, &bytes[offset], plen, &error); + } else if(type == IMQUIC_MOQ_MAX_REQUEST_ID) { + /* Parse this MAX_REQUEST_ID message */ + parsed = imquic_moq_parse_max_request_id(moq, &bytes[offset], plen, &error); + } else if(type == IMQUIC_MOQ_REQUESTS_BLOCKED) { + /* Parse this REQUESTS_BLOCKED message */ parsed = imquic_moq_parse_requests_blocked(moq, &bytes[offset], plen, &error); - } else if(type == IMQUIC_MOQ_REQUEST_OK && moq->version >= IMQUIC_MOQ_VERSION_15 && moq->version <= IMQUIC_MOQ_VERSION_MAX) { + } else if(type == IMQUIC_MOQ_REQUEST_OK) { /* Parse this REQUEST_OK message */ parsed = imquic_moq_parse_request_ok(moq, NULL, &bytes[offset], plen, &error); - } else if(type == IMQUIC_MOQ_REQUEST_ERROR && moq->version >= IMQUIC_MOQ_VERSION_15 && moq->version <= IMQUIC_MOQ_VERSION_MAX) { + } else if(type == IMQUIC_MOQ_REQUEST_ERROR) { /* Parse this REQUEST_ERROR message */ parsed = imquic_moq_parse_request_error(moq, NULL, &bytes[offset], plen, &error); } else if(type == IMQUIC_MOQ_PUBLISH_NAMESPACE) { /* Parse this PUBLISH_NAMESPACE message */ - parsed = imquic_moq_parse_publish_namespace(moq, &bytes[offset], plen, &error); - } else if(type == IMQUIC_MOQ_PUBLISH_NAMESPACE_OK && moq->version >= IMQUIC_MOQ_VERSION_MIN && moq->version <= IMQUIC_MOQ_VERSION_14) { - /* Parse this PUBLISH_NAMESPACE_OK message */ - parsed = imquic_moq_parse_publish_namespace_ok(moq, &bytes[offset], plen, &error); - } else if(type == IMQUIC_MOQ_PUBLISH_NAMESPACE_ERROR && moq->version >= IMQUIC_MOQ_VERSION_MIN && moq->version <= IMQUIC_MOQ_VERSION_14) { - /* Parse this PUBLISH_NAMESPACE_ERROR message */ - parsed = imquic_moq_parse_publish_namespace_error(moq, &bytes[offset], plen, &error); + parsed = imquic_moq_parse_publish_namespace(moq, NULL, &bytes[offset], plen, &error); } else if(type == IMQUIC_MOQ_PUBLISH_NAMESPACE_DONE) { /* Parse this PUBLISH_NAMESPACE_DONE message */ parsed = imquic_moq_parse_publish_namespace_done(moq, &bytes[offset], plen, &error); @@ -1657,79 +1600,53 @@ int imquic_moq_parse_message(imquic_moq_context *moq, uint64_t stream_id, uint8_ parsed = imquic_moq_parse_publish_namespace_cancel(moq, &bytes[offset], plen, &error); } else if(type == IMQUIC_MOQ_PUBLISH) { /* Parse this PUBLISH message */ - parsed = imquic_moq_parse_publish(moq, &bytes[offset], plen, &error); + parsed = imquic_moq_parse_publish(moq, NULL, &bytes[offset], plen, &error); } else if(type == IMQUIC_MOQ_PUBLISH_OK) { /* Parse this PUBLISH_OK message */ - parsed = imquic_moq_parse_publish_ok(moq, &bytes[offset], plen, &error); - } else if(type == IMQUIC_MOQ_PUBLISH_ERROR && moq->version >= IMQUIC_MOQ_VERSION_MIN && moq->version <= IMQUIC_MOQ_VERSION_14) { - /* Parse this PUBLISH_ERROR message */ - parsed = imquic_moq_parse_publish_error(moq, &bytes[offset], plen, &error); + parsed = imquic_moq_parse_publish_ok(moq, NULL, &bytes[offset], plen, &error); } else if(type == IMQUIC_MOQ_SUBSCRIBE) { /* Parse this SUBSCRIBE message */ - parsed = imquic_moq_parse_subscribe(moq, &bytes[offset], plen, &error); + parsed = imquic_moq_parse_subscribe(moq, NULL, &bytes[offset], plen, &error); } else if(type == IMQUIC_MOQ_REQUEST_UPDATE) { /* Parse this REQUEST_UPDATE message */ - parsed = imquic_moq_parse_request_update(moq, &bytes[offset], plen, &error); + parsed = imquic_moq_parse_request_update(moq, NULL, &bytes[offset], plen, &error); } else if(type == IMQUIC_MOQ_SUBSCRIBE_OK) { /* Parse this SUBSCRIBE_OK message */ - parsed = imquic_moq_parse_subscribe_ok(moq, &bytes[offset], plen, &error); - } else if(type == IMQUIC_MOQ_SUBSCRIBE_ERROR && moq->version >= IMQUIC_MOQ_VERSION_MIN && moq->version <= IMQUIC_MOQ_VERSION_14) { - /* Parse this SUBSCRIBE_ERROR message */ - parsed = imquic_moq_parse_subscribe_error(moq, &bytes[offset], plen, &error); + parsed = imquic_moq_parse_subscribe_ok(moq, NULL, &bytes[offset], plen, &error); } else if(type == IMQUIC_MOQ_UNSUBSCRIBE) { /* Parse this UNSUBSCRIBE message */ parsed = imquic_moq_parse_unsubscribe(moq, &bytes[offset], plen, &error); } else if(type == IMQUIC_MOQ_PUBLISH_DONE) { /* Parse this PUBLISH_DONE message */ - parsed = imquic_moq_parse_publish_done(moq, &bytes[offset], plen, &error); - } else if(type == IMQUIC_MOQ_SUBSCRIBE_NAMESPACE && moq->version >= IMQUIC_MOQ_VERSION_MIN && moq->version <= IMQUIC_MOQ_VERSION_15) { - /* Parse this SUBSCRIBE_NAMESPACE message */ - parsed = imquic_moq_parse_subscribe_namespace(moq, NULL, &bytes[offset], plen, &error); - } else if(type == IMQUIC_MOQ_SUBSCRIBE_NAMESPACE_OK && moq->version >= IMQUIC_MOQ_VERSION_MIN && moq->version <= IMQUIC_MOQ_VERSION_14) { - /* Parse this SUBSCRIBE_NAMESPACE_OK message */ - parsed = imquic_moq_parse_subscribe_namespace_ok(moq, &bytes[offset], plen, &error); - } else if(type == IMQUIC_MOQ_SUBSCRIBE_NAMESPACE_ERROR && moq->version >= IMQUIC_MOQ_VERSION_MIN && moq->version <= IMQUIC_MOQ_VERSION_14) { - /* Parse this SUBSCRIBE_NAMESPACE_ERROR message */ - parsed = imquic_moq_parse_subscribe_namespace_error(moq, &bytes[offset], plen, &error); - } else if(type == IMQUIC_MOQ_UNSUBSCRIBE_NAMESPACE && moq->version >= IMQUIC_MOQ_VERSION_MIN && moq->version <= IMQUIC_MOQ_VERSION_15) { - /* Parse this UNSUBSCRIBE_NAMESPACE message */ - parsed = imquic_moq_parse_unsubscribe_namespace(moq, &bytes[offset], plen, &error); + parsed = imquic_moq_parse_publish_done(moq, NULL, &bytes[offset], plen, &error); } else if(type == IMQUIC_MOQ_FETCH) { /* Parse this FETCH message */ - parsed = imquic_moq_parse_fetch(moq, &bytes[offset], plen, &error); + parsed = imquic_moq_parse_fetch(moq, NULL, &bytes[offset], plen, &error); } else if(type == IMQUIC_MOQ_FETCH_CANCEL) { /* Parse this FETCH_CANCEL message */ parsed = imquic_moq_parse_fetch_cancel(moq, &bytes[offset], plen, &error); } else if(type == IMQUIC_MOQ_FETCH_OK) { /* Parse this FETCH_OK message */ - parsed = imquic_moq_parse_fetch_ok(moq, &bytes[offset], plen, &error); - } else if(type == IMQUIC_MOQ_FETCH_ERROR && moq->version >= IMQUIC_MOQ_VERSION_MIN && moq->version <= IMQUIC_MOQ_VERSION_14) { - /* Parse this FETCH_ERROR message */ - parsed = imquic_moq_parse_fetch_error(moq, &bytes[offset], plen, &error); + parsed = imquic_moq_parse_fetch_ok(moq, NULL, &bytes[offset], plen, &error); } else if(type == IMQUIC_MOQ_TRACK_STATUS) { /* Parse this TRACK_STATUS message */ - parsed = imquic_moq_parse_track_status(moq, &bytes[offset], plen, &error); - } else if(type == IMQUIC_MOQ_TRACK_STATUS_OK && moq->version >= IMQUIC_MOQ_VERSION_MIN && moq->version <= IMQUIC_MOQ_VERSION_14) { - /* Parse this TRACK_STATUS_OK message */ - parsed = imquic_moq_parse_track_status_ok(moq, &bytes[offset], plen, &error); - } else if(type == IMQUIC_MOQ_TRACK_STATUS_ERROR && moq->version >= IMQUIC_MOQ_VERSION_MIN && moq->version <= IMQUIC_MOQ_VERSION_14) { - /* Parse this TRACK_STATUS_ERROR message */ - parsed = imquic_moq_parse_track_status_error(moq, &bytes[offset], plen, &error); + parsed = imquic_moq_parse_track_status(moq, NULL, &bytes[offset], plen, &error); } else if(type == IMQUIC_MOQ_GOAWAY) { /* Parse this GOAWAY message */ parsed = imquic_moq_parse_goaway(moq, &bytes[offset], plen, &error); } else { - IMQUIC_LOG(IMQUIC_LOG_WARN, "[%s][MoQ] Unsupported message '%02x'\n", - imquic_get_connection_name(moq->conn), type); + IMQUIC_LOG(IMQUIC_LOG_ERR, "[%s][MoQ] Unsupported message '%02x' (%s)\n", + imquic_get_connection_name(moq->conn), type, + imquic_moq_message_type_str(type, moq->version)); + error = IMQUIC_MOQ_PROTOCOL_VIOLATION; #ifdef HAVE_QLOG if(moq->conn->qlog != NULL && moq->conn->qlog->moq) { json_t *message = imquic_qlog_moq_message_prepare("unknown"); - imquic_moq_qlog_control_message_parsed(moq->conn->qlog, moq->control_stream_id, &bytes[offset], plen, message); + imquic_moq_qlog_control_message_parsed(moq->conn->qlog, stream_id, &bytes[offset], plen, message); } #endif - imquic_buffer_shift(moq->buffer, plen); - return -1; } +next: if(error) { IMQUIC_LOG(IMQUIC_LOG_ERR, "[%s][MoQ] Error parsing MoQ message %s: %s\n", imquic_get_connection_name(moq->conn), @@ -1738,6 +1655,8 @@ int imquic_moq_parse_message(imquic_moq_context *moq, uint64_t stream_id, uint8_ imquic_buffer_shift(moq->buffer, plen); if(error != IMQUIC_MOQ_UNKNOWN_ERROR) imquic_connection_close(moq->conn, error, imquic_moq_error_code_str(error)); + if(moq_stream != NULL) + imquic_refcount_decrease(&moq_stream->ref); return -1; } /* Move to the next message */ @@ -1750,8 +1669,8 @@ int imquic_moq_parse_message(imquic_moq_context *moq, uint64_t stream_id, uint8_ bytes = moq->buffer->bytes; blen = moq->buffer->length; offset = 0; - } else if(moq_stream->subscribe_namespace) { - /* Control message for namespaces advertisement */ + } else if(moq_stream->request_type > 0) { + /* Control message for requests */ size_t plen = blen-offset; tlen = 2; if(blen - offset < tlen) { @@ -1770,9 +1689,39 @@ int imquic_moq_parse_message(imquic_moq_context *moq, uint64_t stream_id, uint8_ imquic_get_connection_name(moq->conn), plen, blen-offset); goto done; } - if(type == IMQUIC_MOQ_SUBSCRIBE_NAMESPACE) { + if(type == IMQUIC_MOQ_PUBLISH_NAMESPACE) { + /* Parse this SPUBLISH_NAMESPACE message */ + parsed = imquic_moq_parse_publish_namespace(moq, moq_stream, &bytes[offset], plen, &error); + } else if(type == IMQUIC_MOQ_SUBSCRIBE_NAMESPACE) { /* Parse this SUBSCRIBE_NAMESPACE message */ parsed = imquic_moq_parse_subscribe_namespace(moq, moq_stream, &bytes[offset], plen, &error); + } else if(type == IMQUIC_MOQ_PUBLISH) { + /* Parse this PUBLISH message */ + parsed = imquic_moq_parse_publish(moq, moq_stream, &bytes[offset], plen, &error); + } else if(type == IMQUIC_MOQ_PUBLISH_OK) { + /* Parse this PUBLISH_OK message */ + parsed = imquic_moq_parse_publish_ok(moq, moq_stream, &bytes[offset], plen, &error); + } else if(type == IMQUIC_MOQ_PUBLISH_DONE) { + /* Parse this PUBLISH_DONE message */ + parsed = imquic_moq_parse_publish_done(moq, moq_stream, &bytes[offset], plen, &error); + } else if(type == IMQUIC_MOQ_SUBSCRIBE) { + /* Parse this SUBSCRIBE message */ + parsed = imquic_moq_parse_subscribe(moq, moq_stream, &bytes[offset], plen, &error); + } else if(type == IMQUIC_MOQ_SUBSCRIBE_OK) { + /* Parse this SUBSCRIBE_OK message */ + parsed = imquic_moq_parse_subscribe_ok(moq, moq_stream, &bytes[offset], plen, &error); + } else if(type == IMQUIC_MOQ_FETCH) { + /* Parse this FETCH message */ + parsed = imquic_moq_parse_fetch(moq, moq_stream, &bytes[offset], plen, &error); + } else if(type == IMQUIC_MOQ_FETCH_OK) { + /* Parse this FETCH_OK message */ + parsed = imquic_moq_parse_fetch_ok(moq, moq_stream, &bytes[offset], plen, &error); + } else if(type == IMQUIC_MOQ_TRACK_STATUS) { + /* Parse this TRACK_STATUS message */ + parsed = imquic_moq_parse_track_status(moq, moq_stream, &bytes[offset], plen, &error); + } else if(type == IMQUIC_MOQ_REQUEST_UPDATE) { + /* Parse this REQUEST_UPDATE message */ + parsed = imquic_moq_parse_request_update(moq, moq_stream, &bytes[offset], plen, &error); } else if(type == IMQUIC_MOQ_REQUEST_OK) { /* Parse this REQUEST_OK message */ parsed = imquic_moq_parse_request_ok(moq, moq_stream, &bytes[offset], plen, &error); @@ -1785,9 +1734,15 @@ int imquic_moq_parse_message(imquic_moq_context *moq, uint64_t stream_id, uint8_ } else if(type == IMQUIC_MOQ_NAMESPACE_DONE) { /* Parse this NAMESPACE_DONE message */ parsed = imquic_moq_parse_namespace_done(moq, moq_stream, &bytes[offset], plen, &error); + } else if(type == IMQUIC_MOQ_PUBLISH_BLOCKED) { + /* Parse this PUBLISH_BLOCKED message */ + parsed = imquic_moq_parse_publish_blocked(moq, moq_stream, &bytes[offset], plen, &error); } else { - IMQUIC_LOG(IMQUIC_LOG_WARN, "[%s][MoQ] Unsupported message '%02x'\n", - imquic_get_connection_name(moq->conn), type); + IMQUIC_LOG(IMQUIC_LOG_WARN, "[%s][MoQ] Unsupported message '%02x' (%s) on a %s request stream\n", + imquic_get_connection_name(moq->conn), type, + imquic_moq_message_type_str(type, moq->version), + imquic_moq_message_type_str(moq_stream->request_type, moq->version)); + error = IMQUIC_MOQ_PROTOCOL_VIOLATION; #ifdef HAVE_QLOG if(moq->conn->qlog != NULL && moq->conn->qlog->moq) { json_t *message = imquic_qlog_moq_message_prepare("unknown"); @@ -1803,6 +1758,8 @@ int imquic_moq_parse_message(imquic_moq_context *moq, uint64_t stream_id, uint8_ imquic_buffer_shift(moq_stream->buffer, plen); if(error != IMQUIC_MOQ_UNKNOWN_ERROR) imquic_connection_close(moq->conn, error, imquic_moq_error_code_str(error)); + if(moq_stream != NULL) + imquic_refcount_decrease(&moq_stream->ref); return -1; } /* Move to the next message */ @@ -1830,17 +1787,21 @@ int imquic_moq_parse_message(imquic_moq_context *moq, uint64_t stream_id, uint8_ } else { IMQUIC_LOG(IMQUIC_LOG_WARN, "[%s][MoQ] Unsupported data message '%02x'\n", imquic_get_connection_name(moq->conn), type); + if(moq_stream != NULL) + imquic_refcount_decrease(&moq_stream->ref); return -1; } } if(parsed == parsed_prev) { IMQUIC_LOG(IMQUIC_LOG_WARN, "[%s][MoQ] Broken MoQ message (didn't advance from offset %zu/%zu)\n", imquic_get_connection_name(moq->conn), parsed, blen); + if(moq_stream != NULL) + imquic_refcount_decrease(&moq_stream->ref); return -1; } } /* Check if we have a media stream to process */ - if(moq_stream != NULL && !moq_stream->subscribe_namespace && blen > offset) { + if(moq_stream != NULL && moq_stream->request_type != IMQUIC_MOQ_SUBSCRIBE_NAMESPACE && blen > offset) { IMQUIC_LOG(IMQUIC_MOQ_LOG_HUGE, "[%s][MoQ] MoQ media stream %"SCNu64" (%zu bytes)\n", imquic_get_connection_name(moq->conn), stream_id, blen - offset); /* Copy the incoming data to the buffer, as we'll use that for parsing */ @@ -1867,27 +1828,25 @@ int imquic_moq_parse_message(imquic_moq_context *moq, uint64_t stream_id, uint8_ /* FIXME Shouldn't happen */ IMQUIC_LOG(IMQUIC_LOG_ERR, "[%s][MoQ] Invalid MoQ stream type '%s' (%02x)\n", imquic_get_connection_name(moq->conn), imquic_moq_data_message_type_str(moq_stream->type, moq->version), moq_stream->type); + if(moq_stream != NULL) + imquic_refcount_decrease(&moq_stream->ref); return -1; } } if(error && error != IMQUIC_MOQ_UNKNOWN_ERROR) { imquic_connection_close(moq->conn, error, imquic_moq_error_code_str(error)); + if(moq_stream != NULL) + imquic_refcount_decrease(&moq_stream->ref); return -1; } } done: if(moq_stream != NULL && complete) { - if(moq_stream->subscribe_namespace) { - /* The SUBSCRIBE_NAMESPACE dedicated bidirectional STREAM has been closed */ - gboolean notify = moq_stream->namespace_publisher; - uint64_t request_id = moq_stream->request_id; - imquic_mutex_lock(&moq->mutex); - g_hash_table_remove(moq->tns_subscriptions_by_id, &moq_stream->request_id); - g_hash_table_remove(moq->streams, &moq_stream->stream_id); /* */ - imquic_mutex_unlock(&moq->mutex); - if(notify && moq->conn->socket && moq->conn->socket->callbacks.moq.incoming_unsubscribe_namespace) - moq->conn->socket->callbacks.moq.incoming_unsubscribe_namespace(moq->conn, request_id, NULL); + if(moq_stream->request_type > 0) { + /* The request dedicated bidirectional STREAM has been closed */ + imquic_moq_request_stream_closed(moq, moq_stream); + imquic_refcount_decrease(&moq_stream->ref); return 0; } IMQUIC_LOG(IMQUIC_MOQ_LOG_HUGE, "[%s][MoQ] Media stream %"SCNu64" is complete\n", @@ -1914,6 +1873,8 @@ int imquic_moq_parse_message(imquic_moq_context *moq, uint64_t stream_id, uint8_ g_hash_table_remove(moq->streams, &stream_id); imquic_mutex_unlock(&moq->mutex); } + if(moq_stream != NULL) + imquic_refcount_decrease(&moq_stream->ref); /* Done */ return 0; } @@ -1921,143 +1882,86 @@ int imquic_moq_parse_message(imquic_moq_context *moq, uint64_t stream_id, uint8_ size_t imquic_moq_parse_client_setup(imquic_moq_context *moq, uint8_t *bytes, size_t blen, uint8_t *error) { if(error) *error = IMQUIC_MOQ_UNKNOWN_ERROR; - if(bytes == NULL || blen < 5) - return 0; - if(!moq->is_server) { - /* Got a CLIENT_SETUP but we're a client */ - IMQUIC_LOG(IMQUIC_LOG_WARN, "[%s][MoQ] Received a CLIENT_SETUP, but we're a client\n", - imquic_get_connection_name(moq->conn)); - if(error) - *error = IMQUIC_MOQ_PROTOCOL_VIOLATION; + if(bytes == NULL || blen < 1) return 0; - } + IMQUIC_MOQ_CHECK_ERR(moq->version >= IMQUIC_MOQ_VERSION_17, error, IMQUIC_MOQ_PROTOCOL_VIOLATION, 0, "CLIENT_SETUP was deprecated"); + IMQUIC_MOQ_CHECK_ERR(!moq->is_server, error, IMQUIC_MOQ_PROTOCOL_VIOLATION, 0, "Received a CLIENT_SETUP, but we're a client"); size_t offset = 0; uint8_t length = 0; - /* Version is only negotiated here for versions up to v14 */ - uint64_t supported_vers = 0, i = 0; - if(moq->version == IMQUIC_MOQ_VERSION_ANY_LEGACY || (moq->version >= IMQUIC_MOQ_VERSION_MIN && moq->version <= IMQUIC_MOQ_VERSION_14)) { - supported_vers = imquic_read_varint(&bytes[offset], blen-offset, &length); - IMQUIC_MOQ_CHECK_ERR(length == 0 || length >= blen-offset, NULL, 0, 0, "Broken CLIENT_SETUP"); - offset += length; - uint64_t version = 0; - gboolean version_set = FALSE; - IMQUIC_LOG(IMQUIC_MOQ_LOG_HUGE, "[%s][MoQ] -- %"SCNu64" supported versions:\n", - imquic_get_connection_name(moq->conn), supported_vers); - g_list_free(moq->supported_versions); - moq->supported_versions = NULL; - for(i = 0; i= blen-offset, NULL, 0, 0, "Broken CLIENT_SETUP"); - IMQUIC_LOG(IMQUIC_MOQ_LOG_HUGE, "[%s][MoQ] -- -- %"SCNu64" (expected %"SCNu32" -- %"SCNu32")\n", - imquic_get_connection_name(moq->conn), version, IMQUIC_MOQ_VERSION_MIN, IMQUIC_MOQ_VERSION_MAX); - if(!version_set) { - if(version == moq->version && moq->version <= IMQUIC_MOQ_VERSION_MAX) { - version_set = TRUE; - IMQUIC_LOG(IMQUIC_MOQ_LOG_HUGE, "[%s][MoQ] -- -- -- Selected version %"SCNu32"\n", - imquic_get_connection_name(moq->conn), moq->version); - } else if((version >= IMQUIC_MOQ_VERSION_MIN && version <= IMQUIC_MOQ_VERSION_14) && moq->version == IMQUIC_MOQ_VERSION_ANY_LEGACY) { - moq->version = version; - version_set = TRUE; - IMQUIC_LOG(IMQUIC_MOQ_LOG_HUGE, "[%s][MoQ] -- -- -- Selected version %"SCNu32"\n", - imquic_get_connection_name(moq->conn), moq->version); - } else { - /* Keep looking */ - version = 0; - } - } - uint32_t v = version; - moq->supported_versions = g_list_prepend(moq->supported_versions, GUINT_TO_POINTER(v)); - offset += length; - } - moq->supported_versions = g_list_reverse(moq->supported_versions); - IMQUIC_MOQ_CHECK_ERR(version == 0, error, IMQUIC_MOQ_VERSION_NEGOTIATION_FAILED, 0, "No supported version"); - } - uint64_t params_num = imquic_read_varint(&bytes[offset], blen-offset, &length); - IMQUIC_MOQ_CHECK_ERR(params_num > 0 && (length == 0 || length >= blen-offset), NULL, 0, 0, "Broken CLIENT_SETUP"); - IMQUIC_MOQ_CHECK_ERR(params_num == 0 && (length == 0 || length > blen-offset), NULL, 0, 0, "Broken CLIENT_SETUP"); + uint64_t opts_num = imquic_read_moqint(moq->version, &bytes[offset], blen-offset, &length); + IMQUIC_MOQ_CHECK_ERR(opts_num > 0 && (length == 0 || length >= blen-offset), NULL, 0, 0, "Broken CLIENT_SETUP"); + IMQUIC_MOQ_CHECK_ERR(opts_num == 0 && (length == 0 || length > blen-offset), NULL, 0, 0, "Broken CLIENT_SETUP"); offset += length; IMQUIC_LOG(IMQUIC_MOQ_LOG_HUGE, "[%s][MoQ] -- %"SCNu64" parameters:\n", - imquic_get_connection_name(moq->conn), params_num); - imquic_moq_setup_parameters parameters = { 0 }; - uint64_t param = 0; - for(i = 0; iconn), opts_num); + imquic_moq_setup_options options = { 0 }; + uint64_t opt = 0, i = 0; + for(i = 0; imax_request_id = parameters.max_request_id; + moq->max_request_id = options.max_request_id; } - if(parameters.max_auth_token_cache_size) { + if(options.max_auth_token_cache_size) { /* Update the value we have */ - moq->max_auth_token_cache_size = parameters.max_auth_token_cache_size; + moq->max_auth_token_cache_size = options.max_auth_token_cache_size; } - if(parameters.moqt_implementation_set && moq->version >= IMQUIC_MOQ_VERSION_14) { + if(options.moqt_implementation_set) { /* Take note of the implemntation */ g_free(moq->peer_implementation); moq->peer_implementation = NULL; - if(strlen(parameters.moqt_implementation) > 0) - moq->peer_implementation = g_strdup(parameters.moqt_implementation); + if(strlen(options.moqt_implementation) > 0) + moq->peer_implementation = g_strdup(options.moqt_implementation); } - if(parameters.path_set) { + if(options.path_set) { /* TODO Handle and validate */ if(moq->conn->http3 != NULL && moq->conn->http3->webtransport) IMQUIC_MOQ_CHECK_ERR(TRUE, error, IMQUIC_MOQ_INVALID_PATH, 0, "PATH received on a WebTransport"); } - if(parameters.authority_set) { + if(options.authority_set) { /* TODO Handle and validate */ if(moq->conn->http3 != NULL && moq->conn->http3->webtransport) IMQUIC_MOQ_CHECK_ERR(TRUE, error, IMQUIC_MOQ_INVALID_PATH, 0, "AUTHORITY received on a WebTransport"); } if(moq->max_request_id == 0) { - IMQUIC_LOG(IMQUIC_MOQ_LOG_HUGE, "[%s][MoQ] No Max Request ID parameter received, setting it to 1\n", + IMQUIC_LOG(IMQUIC_MOQ_LOG_HUGE, "[%s][MoQ] No Max Request ID option received, setting it to 1\n", imquic_get_connection_name(moq->conn)); moq->max_request_id = 1; } #ifdef HAVE_QLOG if(moq->conn->qlog != NULL && moq->conn->qlog->moq) { json_t *message = imquic_qlog_moq_message_prepare("client_setup"); - if(moq->version == IMQUIC_MOQ_VERSION_ANY_LEGACY || (moq->version >= IMQUIC_MOQ_VERSION_MIN && moq->version <= IMQUIC_MOQ_VERSION_14)) { - json_object_set_new(message, "number_of_supported_versions", json_integer(supported_vers)); - json_t *versions = json_array(); - GList *temp = moq->supported_versions; - while(temp) { - json_array_append_new(versions, json_integer(GPOINTER_TO_UINT(temp->data))); - temp = temp->next; - } - json_object_set_new(message, "supported_versions", versions); - } - json_object_set_new(message, "number_of_parameters", json_integer(params_num)); - imquic_qlog_moq_message_add_setup_parameters(message, ¶meters, "setup_parameters"); - imquic_moq_qlog_control_message_parsed(moq->conn->qlog, moq->control_stream_id, bytes-3, offset+3, message); + json_object_set_new(message, "number_of_options", json_integer(opts_num)); + imquic_qlog_moq_message_add_setup_options(message, &options, "setup_options"); + imquic_moq_qlog_control_message_parsed(moq->conn->qlog, imquic_moq_get_control_stream(moq), bytes-3, offset+3, message); } #endif /* Notify the application, if we have a callback */ uint64_t error_code = 0; if(moq->conn->socket && moq->conn->socket->callbacks.moq.incoming_moq_connection) { error_code = moq->conn->socket->callbacks.moq.incoming_moq_connection(moq->conn, - (parameters.auth_token_set ? parameters.auth_token : NULL), - (parameters.auth_token_set ? parameters.auth_token_len : 0)); + (options.auth_token_set ? options.auth_token : NULL), + (options.auth_token_set ? options.auth_token_len : 0)); } IMQUIC_MOQ_CHECK_ERR(error_code > 0, error, error_code, 0, "CLIENT_SETUP rejected by application"); /* If we got here, generate a SERVER_SETUP to send back */ - imquic_moq_setup_parameters s_parameters = { 0 }; + imquic_moq_setup_options s_options = { 0 }; if(moq->local_max_request_id > 0) { - s_parameters.max_request_id_set = TRUE; - s_parameters.max_request_id = moq->local_max_request_id; + s_options.max_request_id_set = TRUE; + s_options.max_request_id = moq->local_max_request_id; } if(moq->local_max_auth_token_cache_size > 0) { - s_parameters.max_auth_token_cache_size_set = TRUE; - s_parameters.max_auth_token_cache_size = moq->local_max_auth_token_cache_size; - } - if(moq->version >= IMQUIC_MOQ_VERSION_14) { - /* FIXME */ - s_parameters.moqt_implementation_set = TRUE; - g_snprintf(s_parameters.moqt_implementation, sizeof(s_parameters.moqt_implementation), "imquic %s", imquic_version_string_full); + s_options.max_auth_token_cache_size_set = TRUE; + s_options.max_auth_token_cache_size = moq->local_max_auth_token_cache_size; } + /* Add the implementation */ + s_options.moqt_implementation_set = TRUE; + g_snprintf(s_options.moqt_implementation, sizeof(s_options.moqt_implementation), "imquic %s", imquic_version_string_full); uint8_t buffer[200]; size_t buflen = sizeof(buffer); - size_t ss_len = imquic_moq_add_server_setup(moq, buffer, buflen, moq->version, &s_parameters); + size_t ss_len = imquic_moq_add_server_setup(moq, buffer, buflen, &s_options); imquic_connection_send_on_stream(moq->conn, moq->control_stream_id, buffer, ss_len, FALSE); g_atomic_int_set(&moq->connected, 1); @@ -2073,80 +1977,59 @@ size_t imquic_moq_parse_client_setup(imquic_moq_context *moq, uint8_t *bytes, si size_t imquic_moq_parse_server_setup(imquic_moq_context *moq, uint8_t *bytes, size_t blen, uint8_t *error) { if(error) *error = IMQUIC_MOQ_UNKNOWN_ERROR; - if(bytes == NULL || blen < 5) + IMQUIC_MOQ_CHECK_ERR(moq->version >= IMQUIC_MOQ_VERSION_17, error, IMQUIC_MOQ_PROTOCOL_VIOLATION, 0, "SERVER_SETUP was deprecated"); + IMQUIC_MOQ_CHECK_ERR(moq->is_server, error, IMQUIC_MOQ_PROTOCOL_VIOLATION, 0, "Received a SERVER_SETUP, but we're a server"); + if(bytes == NULL || blen < 1) return 0; size_t offset = 0; uint8_t length = 0; - /* Version is only negotiated here for versions up to v14 */ - uint64_t version = 0; - if(moq->version == IMQUIC_MOQ_VERSION_ANY_LEGACY || (moq->version >= IMQUIC_MOQ_VERSION_MIN && moq->version <= IMQUIC_MOQ_VERSION_14)) { - IMQUIC_LOG(IMQUIC_MOQ_LOG_HUGE, "[%s][MoQ] -- Supported version:\n", - imquic_get_connection_name(moq->conn)); - version = imquic_read_varint(&bytes[offset], blen-offset, &length); - IMQUIC_MOQ_CHECK_ERR(length == 0 || length >= blen-offset, NULL, 0, 0, "Broken SERVER_SETUP"); - IMQUIC_LOG(IMQUIC_MOQ_LOG_HUGE, "[%s][MoQ] -- -- %"SCNu64" (expected %"SCNu32" -- %"SCNu32")\n", - imquic_get_connection_name(moq->conn), version, IMQUIC_MOQ_VERSION_MIN, IMQUIC_MOQ_VERSION_MAX); - if(version == moq->version && moq->version <= IMQUIC_MOQ_VERSION_MAX) { - IMQUIC_LOG(IMQUIC_MOQ_LOG_HUGE, "[%s][MoQ] -- -- Selected version %"SCNu32"\n", - imquic_get_connection_name(moq->conn), moq->version); - } else if((version >= IMQUIC_MOQ_VERSION_MIN && version <= IMQUIC_MOQ_VERSION_14) && moq->version == IMQUIC_MOQ_VERSION_ANY_LEGACY) { - moq->version = version; - IMQUIC_LOG(IMQUIC_MOQ_LOG_HUGE, "[%s][MoQ] -- -- Selected version %"SCNu32"\n", - imquic_get_connection_name(moq->conn), moq->version); - } else { - IMQUIC_MOQ_CHECK_ERR(version == 0, error, IMQUIC_MOQ_VERSION_NEGOTIATION_FAILED, 0, "No supported version"); - } - offset += length; - } - uint64_t params_num = imquic_read_varint(&bytes[offset], blen-offset, &length); - IMQUIC_MOQ_CHECK_ERR(params_num > 0 && (length == 0 || length >= blen-offset), NULL, 0, 0, "Broken SERVER_SETUP"); - IMQUIC_MOQ_CHECK_ERR(params_num == 0 && (length == 0 || length > blen-offset), NULL, 0, 0, "Broken SERVER_SETUP"); + uint64_t opts_num = imquic_read_moqint(moq->version, &bytes[offset], blen-offset, &length); + IMQUIC_MOQ_CHECK_ERR(opts_num > 0 && (length == 0 || length >= blen-offset), NULL, 0, 0, "Broken SERVER_SETUP"); + IMQUIC_MOQ_CHECK_ERR(opts_num == 0 && (length == 0 || length > blen-offset), NULL, 0, 0, "Broken SERVER_SETUP"); offset += length; - IMQUIC_LOG(IMQUIC_MOQ_LOG_HUGE, "[%s][MoQ] -- %"SCNu64" parameters:\n", - imquic_get_connection_name(moq->conn), params_num); + IMQUIC_LOG(IMQUIC_MOQ_LOG_HUGE, "[%s][MoQ] -- %"SCNu64" options:\n", + imquic_get_connection_name(moq->conn), opts_num); uint64_t i = 0; - imquic_moq_setup_parameters parameters = { 0 }; - uint64_t param = 0; - for(i = 0; imax_request_id = parameters.max_request_id; + moq->max_request_id = options.max_request_id; } - if(parameters.max_auth_token_cache_size_set) { + if(options.max_auth_token_cache_size_set) { /* Update the value we have */ - moq->max_auth_token_cache_size = parameters.max_auth_token_cache_size; + moq->max_auth_token_cache_size = options.max_auth_token_cache_size; } - if(parameters.moqt_implementation_set && moq->version >= IMQUIC_MOQ_VERSION_14) { + if(options.moqt_implementation_set) { /* Take note of the implemntation */ g_free(moq->peer_implementation); moq->peer_implementation = NULL; - if(strlen(parameters.moqt_implementation) > 0) - moq->peer_implementation = g_strdup(parameters.moqt_implementation); + if(strlen(options.moqt_implementation) > 0) + moq->peer_implementation = g_strdup(options.moqt_implementation); } - if(parameters.path_set) { + if(options.path_set) { /* Servers can't use PATH */ - IMQUIC_MOQ_CHECK_ERR(version == 0, error, IMQUIC_MOQ_INVALID_PATH, 0, "PATH received from a server"); + IMQUIC_MOQ_CHECK_ERR(!moq->is_server, error, IMQUIC_MOQ_INVALID_PATH, 0, "PATH received from a server"); } - if(parameters.authority_set) { + if(options.authority_set) { /* Servers can't use AUTHORITY */ - IMQUIC_MOQ_CHECK_ERR(version == 0, error, IMQUIC_MOQ_INVALID_PATH, 0, "AUTHORITY received from a server"); + IMQUIC_MOQ_CHECK_ERR(!moq->is_server, error, IMQUIC_MOQ_INVALID_PATH, 0, "AUTHORITY received from a server"); } if(moq->max_request_id == 0) { - IMQUIC_LOG(IMQUIC_MOQ_LOG_HUGE, "[%s][MoQ] No Max Request ID parameter received, setting it to 1\n", + IMQUIC_LOG(IMQUIC_MOQ_LOG_HUGE, "[%s][MoQ] No Max Request ID option received, setting it to 1\n", imquic_get_connection_name(moq->conn)); moq->max_request_id = 1; } #ifdef HAVE_QLOG if(moq->conn->qlog != NULL && moq->conn->qlog->moq) { json_t *message = imquic_qlog_moq_message_prepare("server_setup"); - if(moq->version == IMQUIC_MOQ_VERSION_ANY_LEGACY || (moq->version >= IMQUIC_MOQ_VERSION_MIN && moq->version <= IMQUIC_MOQ_VERSION_14)) - json_object_set_new(message, "selected_version", json_integer(version)); - json_object_set_new(message, "number_of_parameters", json_integer(params_num)); - imquic_qlog_moq_message_add_setup_parameters(message, ¶meters, "setup_parameters"); - imquic_moq_qlog_control_message_parsed(moq->conn->qlog, moq->control_stream_id, bytes-3, offset+3, message); + json_object_set_new(message, "number_of_options", json_integer(opts_num)); + imquic_qlog_moq_message_add_setup_options(message, &options, "setup_options"); + imquic_moq_qlog_control_message_parsed(moq->conn->qlog, imquic_moq_get_control_stream(moq), bytes-3, offset+3, message); } #endif /* Notify the application the session is ready */ @@ -2158,14 +2041,72 @@ size_t imquic_moq_parse_server_setup(imquic_moq_context *moq, uint8_t *bytes, si return offset; } +size_t imquic_moq_parse_setup(imquic_moq_context *moq, uint8_t *bytes, size_t blen, uint8_t *error) { + if(error) + *error = IMQUIC_MOQ_UNKNOWN_ERROR; + IMQUIC_MOQ_CHECK_ERR(moq->version <= IMQUIC_MOQ_VERSION_16, error, IMQUIC_MOQ_PROTOCOL_VIOLATION, 0, "SETUP unsupported on older versions"); + /* Parse the SETUP options */ + size_t offset = 0; + imquic_moq_setup_options options = { 0 }; + uint64_t opt = 0; + while(bytes != NULL && offset < blen) { + offset += imquic_moq_parse_setup_option(moq, &bytes[offset], blen-offset, &options, &opt, error); + IMQUIC_MOQ_CHECK_ERR(error && *error, NULL, 0, 0, "Broken SETUP"); + } + if(options.max_auth_token_cache_size_set) { + /* Update the value we have */ + moq->max_auth_token_cache_size = options.max_auth_token_cache_size; + } + if(options.moqt_implementation_set) { + /* Take note of the implemntation */ + g_free(moq->peer_implementation); + moq->peer_implementation = NULL; + if(strlen(options.moqt_implementation) > 0) + moq->peer_implementation = g_strdup(options.moqt_implementation); + } + if(options.path_set) { + /* Servers can't use PATH */ + IMQUIC_MOQ_CHECK_ERR(!moq->is_server, error, IMQUIC_MOQ_INVALID_PATH, 0, "PATH received from a server"); + } + if(options.authority_set) { + /* Servers can't use AUTHORITY */ + IMQUIC_MOQ_CHECK_ERR(!moq->is_server, error, IMQUIC_MOQ_INVALID_PATH, 0, "AUTHORITY received from a server"); + } + if(moq->max_request_id == 0) { + IMQUIC_LOG(IMQUIC_MOQ_LOG_HUGE, "[%s][MoQ] No Max Request ID option received, setting it to 1\n", + imquic_get_connection_name(moq->conn)); + moq->max_request_id = 1; + } +#ifdef HAVE_QLOG + if(moq->conn->qlog != NULL && moq->conn->qlog->moq) { + json_t *message = imquic_qlog_moq_message_prepare("setup"); + imquic_qlog_moq_message_add_setup_options(message, &options, "setup_options"); + imquic_moq_qlog_control_message_parsed(moq->conn->qlog, imquic_moq_get_control_stream(moq), bytes-3, offset+3, message); + } +#endif + /* Notify the application the session is ready, if we're done */ + moq->recvd_setup = TRUE; + /* FIXME */ + if(moq->recvd_setup && moq->sent_setup) { + g_atomic_int_set(&moq->connected, 1); + g_atomic_int_set(&moq->check_pending, 1); + if(moq->conn->socket && moq->conn->socket->callbacks.moq.moq_ready) + moq->conn->socket->callbacks.moq.moq_ready(moq->conn); + } + if(error) + *error = 0; + return offset; +} + size_t imquic_moq_parse_max_request_id(imquic_moq_context *moq, uint8_t *bytes, size_t blen, uint8_t *error) { if(error) *error = IMQUIC_MOQ_UNKNOWN_ERROR; + IMQUIC_MOQ_CHECK_ERR(moq->version >= IMQUIC_MOQ_VERSION_17, error, IMQUIC_MOQ_PROTOCOL_VIOLATION, 0, "MAX_REQUEST_ID was deprecated"); if(bytes == NULL || blen < 1) return 0; size_t offset = 0; uint8_t length = 0; - uint64_t max = imquic_read_varint(&bytes[offset], blen-offset, &length) + 1; + uint64_t max = imquic_read_moqint(moq->version, &bytes[offset], blen-offset, &length) + 1; IMQUIC_MOQ_CHECK_ERR(length == 0 || length > blen-offset, NULL, 0, 0, "Broken MAX_REQUEST_ID"); offset += length; IMQUIC_LOG(IMQUIC_MOQ_LOG_HUGE, "[%s][MoQ] -- Maximum Request ID %"SCNu64":\n", @@ -2177,7 +2118,7 @@ size_t imquic_moq_parse_max_request_id(imquic_moq_context *moq, uint8_t *bytes, if(moq->conn->qlog != NULL && moq->conn->qlog->moq) { json_t *message = imquic_qlog_moq_message_prepare("max_request_id"); json_object_set_new(message, "request_id", json_integer(max)); - imquic_moq_qlog_control_message_parsed(moq->conn->qlog, moq->control_stream_id, bytes-3, offset+3, message); + imquic_moq_qlog_control_message_parsed(moq->conn->qlog, imquic_moq_get_control_stream(moq), bytes-3, offset+3, message); } #endif if(error) @@ -2188,11 +2129,12 @@ size_t imquic_moq_parse_max_request_id(imquic_moq_context *moq, uint8_t *bytes, size_t imquic_moq_parse_requests_blocked(imquic_moq_context *moq, uint8_t *bytes, size_t blen, uint8_t *error) { if(error) *error = IMQUIC_MOQ_UNKNOWN_ERROR; + IMQUIC_MOQ_CHECK_ERR(moq->version >= IMQUIC_MOQ_VERSION_17, error, IMQUIC_MOQ_PROTOCOL_VIOLATION, 0, "REQUESTS_BLOCKED was deprecated"); if(bytes == NULL || blen < 1) return 0; size_t offset = 0; uint8_t length = 0; - uint64_t max = imquic_read_varint(&bytes[offset], blen-offset, &length); + uint64_t max = imquic_read_moqint(moq->version, &bytes[offset], blen-offset, &length); IMQUIC_MOQ_CHECK_ERR(length == 0 || length > blen-offset, NULL, 0, 0, "Broken REQUESTS_BLOCKED"); offset += length; IMQUIC_LOG(IMQUIC_MOQ_LOG_HUGE, "[%s][MoQ] -- Maximum Request ID %"SCNu64":\n", @@ -2201,7 +2143,7 @@ size_t imquic_moq_parse_requests_blocked(imquic_moq_context *moq, uint8_t *bytes if(moq->conn->qlog != NULL && moq->conn->qlog->moq) { json_t *message = imquic_qlog_moq_message_prepare("requests_blocked"); json_object_set_new(message, "maximum_request_id", json_integer(max)); - imquic_moq_qlog_control_message_parsed(moq->conn->qlog, moq->control_stream_id, bytes-3, offset+3, message); + imquic_moq_qlog_control_message_parsed(moq->conn->qlog, imquic_moq_get_control_stream(moq), bytes-3, offset+3, message); } #endif /* Notify the application */ @@ -2217,17 +2159,24 @@ size_t imquic_moq_parse_request_ok(imquic_moq_context *moq, imquic_moq_stream *m *error = IMQUIC_MOQ_UNKNOWN_ERROR; if(bytes == NULL || blen < 1) return 0; - IMQUIC_MOQ_CHECK_ERR((moq->version >= IMQUIC_MOQ_VERSION_16 && moq_stream != NULL && - (moq_stream->namespace_publisher || !g_atomic_int_compare_and_exchange(&moq_stream->subscribe_namespace_state, 1, 2))), - error, IMQUIC_MOQ_PROTOCOL_VIOLATION, 0, "Invalid use of REQUEST_OK for SUBSCRIBE_NAMESPACE"); + /* FIXME State management needs to be fixed, because an update will trigger OK/ERROR too */ + IMQUIC_MOQ_CHECK_ERR((moq->version >= IMQUIC_MOQ_VERSION_17 && (moq_stream == NULL || moq_stream->request_type == 0 || + !moq_stream->request_sender || (moq_stream->request_state != IMQUIC_MOQ_REQUEST_STATE_SENT && moq_stream->request_state != IMQUIC_MOQ_REQUEST_STATE_UPDATE_SENT))), + error, IMQUIC_MOQ_PROTOCOL_VIOLATION, 0, "Invalid use of REQUEST_OK on bidirectional request"); size_t offset = 0; uint8_t length = 0; - uint64_t request_id = imquic_read_varint(&bytes[offset], blen-offset, &length); - IMQUIC_MOQ_CHECK_ERR(length == 0 || length > blen-offset, NULL, 0, 0, "Broken REQUEST_OK"); - offset += length; - IMQUIC_LOG(IMQUIC_MOQ_LOG_HUGE, "[%s][MoQ] -- Request ID: %"SCNu64"\n", - imquic_get_connection_name(moq->conn), request_id); - uint64_t params_num = imquic_read_varint(&bytes[offset], blen-offset, &length); + uint64_t request_id = 0; + if(moq->version <= IMQUIC_MOQ_VERSION_16) { + request_id = imquic_read_moqint(moq->version, &bytes[offset], blen-offset, &length); + IMQUIC_MOQ_CHECK_ERR(length == 0 || length > blen-offset, NULL, 0, 0, "Broken REQUEST_OK"); + offset += length; + IMQUIC_LOG(IMQUIC_MOQ_LOG_HUGE, "[%s][MoQ] -- Request ID: %"SCNu64"\n", + imquic_get_connection_name(moq->conn), request_id); + } else { + request_id = moq_stream->update_request_id; + moq_stream->update_request_id = 0; + } + uint64_t params_num = imquic_read_moqint(moq->version, &bytes[offset], blen-offset, &length); IMQUIC_MOQ_CHECK_ERR(params_num > 0 && (length == 0 || length >= blen-offset), NULL, 0, 0, "Broken REQUEST_OK"); IMQUIC_MOQ_CHECK_ERR(params_num == 0 && (length == 0 || length > blen-offset), NULL, 0, 0, "Broken REQUEST_OK"); offset += length; @@ -2238,20 +2187,23 @@ size_t imquic_moq_parse_request_ok(imquic_moq_context *moq, imquic_moq_stream *m for(i = 0; iconn->qlog != NULL && moq->conn->qlog->moq) { json_t *message = imquic_qlog_moq_message_prepare("request_ok"); - json_object_set_new(message, "request_id", json_integer(request_id)); + if(moq->version <= IMQUIC_MOQ_VERSION_16) + json_object_set_new(message, "request_id", json_integer(request_id)); json_object_set_new(message, "number_of_parameters", json_integer(params_num)); imquic_qlog_moq_message_add_request_parameters(message, moq->version, ¶meters, "parameters"); imquic_moq_qlog_control_message_parsed(moq->conn->qlog, - (moq_stream ? moq_stream->stream_id : moq->control_stream_id), bytes-3, offset+3, message); + (moq_stream ? moq_stream->stream_id : imquic_moq_get_control_stream(moq)), bytes-3, offset+3, message); } #endif /* Notify the application, but we'll need to check which callback to trigger */ imquic_mutex_lock(&moq->mutex); + if(moq_stream != NULL) + moq_stream->request_state = IMQUIC_MOQ_REQUEST_STATE_OK; imquic_moq_message_type type = GPOINTER_TO_UINT(g_hash_table_lookup(moq->requests, &request_id)); g_hash_table_remove(moq->requests, &request_id); imquic_mutex_unlock(&moq->mutex); @@ -2270,11 +2222,11 @@ size_t imquic_moq_parse_request_ok(imquic_moq_context *moq, imquic_moq_stream *m break; case IMQUIC_MOQ_TRACK_STATUS: if(moq->conn->socket && moq->conn->socket->callbacks.moq.track_status_accepted) - moq->conn->socket->callbacks.moq.track_status_accepted(moq->conn, request_id, 0, ¶meters); + moq->conn->socket->callbacks.moq.track_status_accepted(moq->conn, request_id, ¶meters); break; default: - IMQUIC_LOG(IMQUIC_LOG_WARN, "[%s][MoQ] Couldn't find a request associated to ID %"SCNu64" (type %d), can't notify success\n", - imquic_get_connection_name(moq->conn), request_id, type); + IMQUIC_LOG(IMQUIC_LOG_WARN, "[%s][MoQ] Couldn't find a request associated to ID %"SCNu64" (%s), can't notify success\n", + imquic_get_connection_name(moq->conn), request_id, imquic_moq_message_type_str(type, moq->version)); break; } if(error) @@ -2287,30 +2239,34 @@ size_t imquic_moq_parse_request_error(imquic_moq_context *moq, imquic_moq_stream *error = IMQUIC_MOQ_UNKNOWN_ERROR; if(bytes == NULL || blen < 1) return 0; - IMQUIC_MOQ_CHECK_ERR((moq->version >= IMQUIC_MOQ_VERSION_16 && moq_stream != NULL && - (moq_stream->namespace_publisher || !g_atomic_int_compare_and_exchange(&moq_stream->subscribe_namespace_state, 1, 2))), - error, IMQUIC_MOQ_PROTOCOL_VIOLATION, 0, "Invalid use of REQUEST_ERROR for SUBSCRIBE_NAMESPACE"); + /* FIXME State management needs to be fixed, because an update will trigger OK/ERROR too */ + IMQUIC_MOQ_CHECK_ERR((moq->version >= IMQUIC_MOQ_VERSION_17 && (moq_stream == NULL || moq_stream->request_type == 0 || + !moq_stream->request_sender || (moq_stream->request_state != IMQUIC_MOQ_REQUEST_STATE_SENT && moq_stream->request_state != IMQUIC_MOQ_REQUEST_STATE_UPDATE_SENT))), + error, IMQUIC_MOQ_PROTOCOL_VIOLATION, 0, "Invalid use of REQUEST_ERROR on bidirectional request"); size_t offset = 0; uint8_t length = 0; - uint64_t request_id = imquic_read_varint(&bytes[offset], blen-offset, &length); - IMQUIC_MOQ_CHECK_ERR(length == 0 || length >= blen-offset, NULL, 0, 0, "Broken REQUEST_ERROR"); - offset += length; - IMQUIC_LOG(IMQUIC_MOQ_LOG_HUGE, "[%s][MoQ] -- Request ID: %"SCNu64"\n", - imquic_get_connection_name(moq->conn), request_id); - uint64_t error_code = imquic_read_varint(&bytes[offset], blen-offset, &length); + uint64_t request_id = 0; + if(moq->version <= IMQUIC_MOQ_VERSION_16) { + request_id = imquic_read_moqint(moq->version, &bytes[offset], blen-offset, &length); + IMQUIC_MOQ_CHECK_ERR(length == 0 || length > blen-offset, NULL, 0, 0, "Broken REQUEST_ERROR"); + offset += length; + IMQUIC_LOG(IMQUIC_MOQ_LOG_HUGE, "[%s][MoQ] -- Request ID: %"SCNu64"\n", + imquic_get_connection_name(moq->conn), request_id); + } else { + request_id = moq_stream->update_request_id; + moq_stream->update_request_id = 0; + } + uint64_t error_code = imquic_read_moqint(moq->version, &bytes[offset], blen-offset, &length); IMQUIC_MOQ_CHECK_ERR(length == 0 || length >= blen-offset, NULL, 0, 0, "Broken REQUEST_ERROR"); offset += length; IMQUIC_LOG(IMQUIC_MOQ_LOG_HUGE, "[%s][MoQ] -- Error Code: %s (%"SCNu64")\n", imquic_get_connection_name(moq->conn), imquic_moq_error_code_str(error_code), error_code); - uint64_t retry_interval = 0; - if(moq->version >= IMQUIC_MOQ_VERSION_16) { - retry_interval = imquic_read_varint(&bytes[offset], blen-offset, &length); - IMQUIC_MOQ_CHECK_ERR(length == 0 || length >= blen-offset, NULL, 0, 0, "Broken REQUEST_ERROR"); - offset += length; - IMQUIC_LOG(IMQUIC_MOQ_LOG_HUGE, "[%s][MoQ] -- Retry Interval: %"SCNu64"\n", - imquic_get_connection_name(moq->conn), retry_interval); - } - uint64_t rs_len = imquic_read_varint(&bytes[offset], blen-offset, &length); + uint64_t retry_interval = imquic_read_moqint(moq->version, &bytes[offset], blen-offset, &length); + IMQUIC_MOQ_CHECK_ERR(length == 0 || length >= blen-offset, NULL, 0, 0, "Broken REQUEST_ERROR"); + offset += length; + IMQUIC_LOG(IMQUIC_MOQ_LOG_HUGE, "[%s][MoQ] -- Retry Interval: %"SCNu64"\n", + imquic_get_connection_name(moq->conn), retry_interval); + uint64_t rs_len = imquic_read_moqint(moq->version, &bytes[offset], blen-offset, &length); IMQUIC_MOQ_CHECK_ERR(length == 0 || length > blen-offset, NULL, 0, 0, "Broken REQUEST_ERROR"); offset += length; char reason[1024], *reason_str = NULL; @@ -2329,18 +2285,22 @@ size_t imquic_moq_parse_request_error(imquic_moq_context *moq, imquic_moq_stream #ifdef HAVE_QLOG if(moq->conn->qlog != NULL && moq->conn->qlog->moq) { json_t *message = imquic_qlog_moq_message_prepare("request_error"); - json_object_set_new(message, "request_id", json_integer(request_id)); + if(moq->version <= IMQUIC_MOQ_VERSION_16) + json_object_set_new(message, "request_id", json_integer(request_id)); json_object_set_new(message, "error_code", json_integer(error_code)); - if(moq->version >= IMQUIC_MOQ_VERSION_16) - json_object_set_new(message, "retry_interval", json_integer(retry_interval)); + json_object_set_new(message, "retry_interval", json_integer(retry_interval)); if(reason_str != NULL) json_object_set_new(message, "reason", json_string(reason_str)); imquic_moq_qlog_control_message_parsed(moq->conn->qlog, - (moq_stream ? moq_stream->stream_id : moq->control_stream_id), bytes-3, offset+3, message); + (moq_stream ? moq_stream->stream_id : imquic_moq_get_control_stream(moq)), bytes-3, offset+3, message); } #endif /* Notify the application, but we'll need to check which callback to trigger */ imquic_mutex_lock(&moq->mutex); + if(moq_stream != NULL) { + moq_stream->request_state = (moq_stream->request_state == IMQUIC_MOQ_REQUEST_STATE_UPDATE_SENT) ? + IMQUIC_MOQ_REQUEST_STATE_OK : IMQUIC_MOQ_REQUEST_STATE_ERROR; + } imquic_moq_message_type type = GPOINTER_TO_UINT(g_hash_table_lookup(moq->requests, &request_id)); g_hash_table_remove(moq->requests, &request_id); imquic_mutex_unlock(&moq->mutex); @@ -2359,7 +2319,7 @@ size_t imquic_moq_parse_request_error(imquic_moq_context *moq, imquic_moq_stream break; case IMQUIC_MOQ_SUBSCRIBE: if(moq->conn->socket && moq->conn->socket->callbacks.moq.subscribe_error) - moq->conn->socket->callbacks.moq.subscribe_error(moq->conn, request_id, error_code, reason_str, 0, retry_interval); + moq->conn->socket->callbacks.moq.subscribe_error(moq->conn, request_id, error_code, reason_str, retry_interval); break; case IMQUIC_MOQ_REQUEST_UPDATE: if(moq->conn->socket && moq->conn->socket->callbacks.moq.request_update_error) @@ -2374,8 +2334,8 @@ size_t imquic_moq_parse_request_error(imquic_moq_context *moq, imquic_moq_stream moq->conn->socket->callbacks.moq.track_status_error(moq->conn, request_id, error_code, reason_str, retry_interval); break; default: - IMQUIC_LOG(IMQUIC_LOG_WARN, "[%s][MoQ] Couldn't find a request associated to ID %"SCNu64" (type %d), can't notify error\n", - imquic_get_connection_name(moq->conn), request_id, type); + IMQUIC_LOG(IMQUIC_LOG_WARN, "[%s][MoQ] Couldn't find a request associated to ID %"SCNu64" (%s), can't notify error\n", + imquic_get_connection_name(moq->conn), request_id, imquic_moq_message_type_str(type, moq->version)); break; } if(error) @@ -2383,23 +2343,34 @@ size_t imquic_moq_parse_request_error(imquic_moq_context *moq, imquic_moq_stream return offset; } -size_t imquic_moq_parse_publish_namespace(imquic_moq_context *moq, uint8_t *bytes, size_t blen, uint8_t *error) { +size_t imquic_moq_parse_publish_namespace(imquic_moq_context *moq, imquic_moq_stream *moq_stream, uint8_t *bytes, size_t blen, uint8_t *error) { if(error) *error = IMQUIC_MOQ_UNKNOWN_ERROR; if(bytes == NULL || blen < 1) return 0; + IMQUIC_MOQ_CHECK_ERR((moq->version >= IMQUIC_MOQ_VERSION_17 && (moq_stream == NULL || + moq_stream->request_sender || moq_stream->request_state != IMQUIC_MOQ_REQUEST_STATE_NEW)), + error, IMQUIC_MOQ_PROTOCOL_VIOLATION, 0, "Invalid use of PUBLISH_NAMESPACE on bidirectional request"); size_t offset = 0; uint8_t length = 0; - uint64_t request_id = imquic_read_varint(&bytes[offset], blen-offset, &length); + uint64_t request_id = imquic_read_moqint(moq->version, &bytes[offset], blen-offset, &length); IMQUIC_MOQ_CHECK_ERR(length == 0 || length >= blen-offset, NULL, 0, 0, "Broken PUBLISH_NAMESPACE"); offset += length; IMQUIC_LOG(IMQUIC_MOQ_LOG_HUGE, "[%s][MoQ] -- Request ID: %"SCNu64"\n", imquic_get_connection_name(moq->conn), request_id); + uint64_t required_id_delta = 0; + if(moq->version >= IMQUIC_MOQ_VERSION_17) { + required_id_delta = imquic_read_moqint(moq->version, &bytes[offset], blen-offset, &length); + IMQUIC_MOQ_CHECK_ERR(length == 0 || length >= blen-offset, NULL, 0, 0, "Broken PUBLISH_NAMESPACE"); + offset += length; + IMQUIC_LOG(IMQUIC_MOQ_LOG_HUGE, "[%s][MoQ] -- Required Request ID Delta: %"SCNu64"\n", + imquic_get_connection_name(moq->conn), required_id_delta); + } imquic_moq_namespace tns[32]; memset(&tns, 0, sizeof(tns)); uint64_t tns_num = 0, i = 0; IMQUIC_MOQ_PARSE_NAMESPACES(IMQUIC_MOQ_PUBLISH_NAMESPACE, tns_num, i, "Broken PUBLISH_NAMESPACE", FALSE); - uint64_t params_num = imquic_read_varint(&bytes[offset], blen-offset, &length); + uint64_t params_num = imquic_read_moqint(moq->version, &bytes[offset], blen-offset, &length); IMQUIC_MOQ_CHECK_ERR(params_num > 0 && (length == 0 || length >= blen-offset), NULL, 0, 0, "Broken PUBLISH_NAMESPACE"); IMQUIC_MOQ_CHECK_ERR(params_num == 0 && (length == 0 || length > blen-offset), NULL, 0, 0, "Broken PUBLISH_NAMESPACE"); offset += length; @@ -2410,24 +2381,37 @@ size_t imquic_moq_parse_publish_namespace(imquic_moq_context *moq, uint8_t *byte for(i = 0; iconn->qlog != NULL && moq->conn->qlog->moq) { + if(moq_stream != NULL) + imquic_moq_qlog_stream_type_set(moq->conn->qlog, FALSE, moq_stream->stream_id, "publish_namespace"); json_t *message = imquic_qlog_moq_message_prepare("publish_namespace"); json_object_set_new(message, "request_id", json_integer(request_id)); + if(moq->version >= IMQUIC_MOQ_VERSION_17) + json_object_set_new(message, "required_request_id_delta", json_integer(required_id_delta)); imquic_qlog_moq_message_add_namespace(message, &tns[0], "track_namespace"); json_object_set_new(message, "number_of_parameters", json_integer(params_num)); imquic_qlog_moq_message_add_request_parameters(message, moq->version, ¶meters, "parameters"); - imquic_moq_qlog_control_message_parsed(moq->conn->qlog, moq->control_stream_id, bytes-3, offset+3, message); + imquic_moq_qlog_control_message_parsed(moq->conn->qlog, + (moq_stream ? moq_stream->stream_id : imquic_moq_get_control_stream(moq)), bytes-3, offset+3, message); } #endif /* Make sure this is in line with the expected request ID */ IMQUIC_MOQ_CHECK_ERR(!moq_is_request_id_valid(moq, request_id, FALSE), error, IMQUIC_MOQ_INVALID_REQUEST_ID, 0, "Invalid Request ID"); moq->expected_request_id = request_id + IMQUIC_MOQ_REQUEST_ID_INCREMENT; + /* If we're on a recent version of MoQ, track this request via its ID */ + if(moq_stream != NULL) { + moq_stream->request_id = request_id; + moq_stream->request_state = IMQUIC_MOQ_REQUEST_STATE_SENT; + imquic_mutex_lock(&moq->mutex); + g_hash_table_insert(moq->streams_by_reqid, imquic_dup_uint64(request_id), moq_stream); + imquic_mutex_unlock(&moq->mutex); + } /* Notify the application */ if(moq->conn->socket && moq->conn->socket->callbacks.moq.incoming_publish_namespace) { - moq->conn->socket->callbacks.moq.incoming_publish_namespace(moq->conn, request_id, &tns[0], ¶meters); + moq->conn->socket->callbacks.moq.incoming_publish_namespace(moq->conn, request_id, required_id_delta, &tns[0], ¶meters); } else { /* No handler for this request, let's reject it ourselves */ imquic_moq_reject_publish_namespace(moq->conn, request_id, IMQUIC_MOQ_REQERR_NOT_SUPPORTED, "Not handled", 0); @@ -2437,89 +2421,6 @@ size_t imquic_moq_parse_publish_namespace(imquic_moq_context *moq, uint8_t *byte return offset; } -size_t imquic_moq_parse_publish_namespace_ok(imquic_moq_context *moq, uint8_t *bytes, size_t blen, uint8_t *error) { - if(error) - *error = IMQUIC_MOQ_UNKNOWN_ERROR; - if(bytes == NULL || blen < 1) - return 0; - size_t offset = 0; - uint8_t length = 0; - uint64_t request_id = imquic_read_varint(&bytes[offset], blen-offset, &length); - IMQUIC_MOQ_CHECK_ERR(length == 0 || length > blen-offset, NULL, 0, 0, "Broken PUBLISH_NAMESPACE_OK"); - offset += length; - IMQUIC_LOG(IMQUIC_MOQ_LOG_HUGE, "[%s][MoQ] -- Request ID: %"SCNu64"\n", - imquic_get_connection_name(moq->conn), request_id); -#ifdef HAVE_QLOG - if(moq->conn->qlog != NULL && moq->conn->qlog->moq) { - json_t *message = imquic_qlog_moq_message_prepare(moq->version >= IMQUIC_MOQ_VERSION_15 ? "request_ok" : "publish_namespace_ok"); - json_object_set_new(message, "request_id", json_integer(request_id)); - imquic_moq_qlog_control_message_parsed(moq->conn->qlog, moq->control_stream_id, bytes-3, offset+3, message); - } -#endif - /* Notify the application */ - if(moq->conn->socket && moq->conn->socket->callbacks.moq.publish_namespace_accepted) { - imquic_moq_request_parameters params; - imquic_moq_request_parameters_init_defaults(¶ms); - moq->conn->socket->callbacks.moq.publish_namespace_accepted(moq->conn, request_id, ¶ms); - } - if(error) - *error = 0; - return offset; -} - -size_t imquic_moq_parse_publish_namespace_error(imquic_moq_context *moq, uint8_t *bytes, size_t blen, uint8_t *error) { - if(error) - *error = IMQUIC_MOQ_UNKNOWN_ERROR; - if(bytes == NULL || blen < 1) - return 0; - size_t offset = 0; - uint8_t length = 0; - uint64_t request_id = imquic_read_varint(&bytes[offset], blen-offset, &length); - IMQUIC_MOQ_CHECK_ERR(length == 0 || length >= blen-offset, NULL, 0, 0, "Broken PUBLISH_NAMESPACE_ERROR"); - offset += length; - IMQUIC_LOG(IMQUIC_MOQ_LOG_HUGE, "[%s][MoQ] -- Request ID: %"SCNu64"\n", - imquic_get_connection_name(moq->conn), request_id); - uint64_t error_code = imquic_read_varint(&bytes[offset], blen-offset, &length); - IMQUIC_MOQ_CHECK_ERR(length == 0 || length >= blen-offset, NULL, 0, 0, "Broken PUBLISH_NAMESPACE_ERROR"); - offset += length; - IMQUIC_LOG(IMQUIC_MOQ_LOG_HUGE, "[%s][MoQ] -- Error Code: %s (%"SCNu64")\n", - imquic_get_connection_name(moq->conn), imquic_moq_error_code_str(error_code), error_code); - uint64_t rs_len = imquic_read_varint(&bytes[offset], blen-offset, &length); - IMQUIC_MOQ_CHECK_ERR(length == 0 || length > blen-offset, NULL, 0, 0, "Broken PUBLISH_NAMESPACE_ERROR"); - offset += length; - char reason[1024], *reason_str = NULL; - if(rs_len > 0) { - IMQUIC_MOQ_CHECK_ERR(rs_len > blen-offset, NULL, 0, 0, "Broken PUBLISH_NAMESPACE_ERROR"); - IMQUIC_MOQ_CHECK_ERR(rs_len > sizeof(reason), error, IMQUIC_MOQ_PROTOCOL_VIOLATION, 0, "Invalid reason length"); - int reason_len = (int)rs_len; - IMQUIC_LOG(IMQUIC_MOQ_LOG_HUGE, "[%s][MoQ] -- Reason Phrase: %.*s\n", - imquic_get_connection_name(moq->conn), reason_len, &bytes[offset]); - if(reason_len > 0) { - g_snprintf(reason, sizeof(reason), "%.*s", reason_len, &bytes[offset]); - reason_str = reason; - } - offset += reason_len; - } -#ifdef HAVE_QLOG - if(moq->conn->qlog != NULL && moq->conn->qlog->moq) { - json_t *message = imquic_qlog_moq_message_prepare(moq->version >= IMQUIC_MOQ_VERSION_15 ? "request_error" : "publish_namespace_error"); - json_object_set_new(message, "request_id", json_integer(request_id)); - json_object_set_new(message, "error_code", json_integer(error_code)); - if(reason_str != NULL) - json_object_set_new(message, "reason", json_string(reason_str)); - imquic_moq_qlog_control_message_parsed(moq->conn->qlog, moq->control_stream_id, bytes-3, offset+3, message); - } -#endif - /* Notify the application */ - if(moq->conn->socket && moq->conn->socket->callbacks.moq.publish_namespace_error) { - moq->conn->socket->callbacks.moq.publish_namespace_error(moq->conn, - request_id, error_code, reason_str, 0); - } - if(error) - *error = 0; - return offset; -} - size_t imquic_moq_parse_publish_namespace_done(imquic_moq_context *moq, uint8_t *bytes, size_t blen, uint8_t *error) { if(error) *error = IMQUIC_MOQ_UNKNOWN_ERROR; @@ -2527,20 +2428,19 @@ size_t imquic_moq_parse_publish_namespace_done(imquic_moq_context *moq, uint8_t return 0; size_t offset = 0; uint8_t length = 0; - imquic_moq_namespace tns[32]; - memset(&tns, 0, sizeof(tns)); - uint64_t tns_num = 0, i = 0; - IMQUIC_MOQ_PARSE_NAMESPACES(IMQUIC_MOQ_PUBLISH_NAMESPACE_DONE, tns_num, i, "Broken PUBLISH_NAMESPACE_DONE", TRUE); + uint64_t request_id = imquic_read_moqint(moq->version, &bytes[offset], blen-offset, &length); + IMQUIC_MOQ_CHECK_ERR(length == 0 || length >= blen-offset, NULL, 0, 0, "Broken PUBLISH_NAMESPACE_DONE"); + offset += length; #ifdef HAVE_QLOG if(moq->conn->qlog != NULL && moq->conn->qlog->moq) { json_t *message = imquic_qlog_moq_message_prepare("publish_namespace_done"); - imquic_qlog_moq_message_add_namespace(message, &tns[0], "track_namespace"); - imquic_moq_qlog_control_message_parsed(moq->conn->qlog, moq->control_stream_id, bytes-3, offset+3, message); + json_object_set_new(message, "request_id", json_integer(request_id)); + imquic_moq_qlog_control_message_parsed(moq->conn->qlog, imquic_moq_get_control_stream(moq), bytes-3, offset+3, message); } #endif /* Notify the application */ if(moq->conn->socket && moq->conn->socket->callbacks.moq.publish_namespace_done) - moq->conn->socket->callbacks.moq.publish_namespace_done(moq->conn, &tns[0]); + moq->conn->socket->callbacks.moq.publish_namespace_done(moq->conn, request_id); if(error) *error = 0; return offset; @@ -2553,20 +2453,18 @@ size_t imquic_moq_parse_publish_namespace_cancel(imquic_moq_context *moq, uint8_ return 0; size_t offset = 0; uint8_t length = 0; - imquic_moq_namespace tns[32]; - memset(&tns, 0, sizeof(tns)); - uint64_t error_code = 0; - char reason[1024], *reason_str = NULL; - uint64_t tns_num = 0, i = 0; - IMQUIC_MOQ_PARSE_NAMESPACES(IMQUIC_MOQ_PUBLISH_NAMESPACE_DONE, tns_num, i, "Broken PUBLISH_NAMESPACE_CANCEL", FALSE); - error_code = imquic_read_varint(&bytes[offset], blen-offset, &length); + uint64_t request_id = imquic_read_moqint(moq->version, &bytes[offset], blen-offset, &length); + IMQUIC_MOQ_CHECK_ERR(length == 0 || length >= blen-offset, NULL, 0, 0, "Broken PUBLISH_NAMESPACE"); + offset += length; + uint64_t error_code = imquic_read_moqint(moq->version, &bytes[offset], blen-offset, &length); IMQUIC_MOQ_CHECK_ERR(length == 0 || length >= blen-offset, NULL, 0, 0, "Broken PUBLISH_NAMESPACE_CANCEL"); offset += length; IMQUIC_LOG(IMQUIC_MOQ_LOG_HUGE, "[%s][MoQ] -- Error Code: %s (%"SCNu64")\n", imquic_get_connection_name(moq->conn), imquic_moq_error_code_str(error_code), error_code); - uint64_t rs_len = imquic_read_varint(&bytes[offset], blen-offset, &length); + uint64_t rs_len = imquic_read_moqint(moq->version, &bytes[offset], blen-offset, &length); IMQUIC_MOQ_CHECK_ERR(length == 0 || length > blen-offset, NULL, 0, 0, "Broken PUBLISH_NAMESPACE_CANCEL"); offset += length; + char reason[1024], *reason_str = NULL; if(rs_len > 0) { IMQUIC_MOQ_CHECK_ERR(rs_len > blen-offset, NULL, 0, 0, "Broken PUBLISH_NAMESPACE_CANCEL"); IMQUIC_MOQ_CHECK_ERR(rs_len > sizeof(reason), error, IMQUIC_MOQ_PROTOCOL_VIOLATION, 0, "Invalid reason length"); @@ -2582,87 +2480,58 @@ size_t imquic_moq_parse_publish_namespace_cancel(imquic_moq_context *moq, uint8_ #ifdef HAVE_QLOG if(moq->conn->qlog != NULL && moq->conn->qlog->moq) { json_t *message = imquic_qlog_moq_message_prepare("publish_namespace_cancel"); - imquic_qlog_moq_message_add_namespace(message, &tns[0], "track_namespace"); + json_object_set_new(message, "request_id", json_integer(request_id)); json_object_set_new(message, "error_code", json_integer(error_code)); if(reason_str != NULL) json_object_set_new(message, "reason", json_string(reason_str)); - imquic_moq_qlog_control_message_parsed(moq->conn->qlog, moq->control_stream_id, bytes-3, offset+3, message); + imquic_moq_qlog_control_message_parsed(moq->conn->qlog, imquic_moq_get_control_stream(moq), bytes-3, offset+3, message); } #endif /* Notify the application */ if(moq->conn->socket && moq->conn->socket->callbacks.moq.incoming_publish_namespace_cancel) - moq->conn->socket->callbacks.moq.incoming_publish_namespace_cancel(moq->conn, &tns[0], error_code, reason); + moq->conn->socket->callbacks.moq.incoming_publish_namespace_cancel(moq->conn, request_id, error_code, reason); if(error) *error = 0; return offset; } -size_t imquic_moq_parse_publish(imquic_moq_context *moq, uint8_t *bytes, size_t blen, uint8_t *error) { +size_t imquic_moq_parse_publish(imquic_moq_context *moq, imquic_moq_stream *moq_stream, uint8_t *bytes, size_t blen, uint8_t *error) { if(error) *error = IMQUIC_MOQ_UNKNOWN_ERROR; if(bytes == NULL || blen < 1) return 0; + IMQUIC_MOQ_CHECK_ERR((moq->version >= IMQUIC_MOQ_VERSION_17 && (moq_stream == NULL || + moq_stream->request_sender || moq_stream->request_state != IMQUIC_MOQ_REQUEST_STATE_NEW)), + error, IMQUIC_MOQ_PROTOCOL_VIOLATION, 0, "Invalid use of PUBLISH on bidirectional request"); size_t offset = 0; uint8_t length = 0; - uint64_t request_id = imquic_read_varint(&bytes[offset], blen-offset, &length); + uint64_t request_id = imquic_read_moqint(moq->version, &bytes[offset], blen-offset, &length); IMQUIC_MOQ_CHECK_ERR(length == 0 || length >= blen-offset, NULL, 0, 0, "Broken PUBLISH"); offset += length; IMQUIC_LOG(IMQUIC_MOQ_LOG_HUGE, "[%s][MoQ] -- Request ID: %"SCNu64"\n", imquic_get_connection_name(moq->conn), request_id); + uint64_t required_id_delta = 0; + if(moq->version >= IMQUIC_MOQ_VERSION_17) { + required_id_delta = imquic_read_moqint(moq->version, &bytes[offset], blen-offset, &length); + IMQUIC_MOQ_CHECK_ERR(length == 0 || length >= blen-offset, NULL, 0, 0, "Broken PUBLISH"); + offset += length; + IMQUIC_LOG(IMQUIC_MOQ_LOG_HUGE, "[%s][MoQ] -- Required Request ID Delta: %"SCNu64"\n", + imquic_get_connection_name(moq->conn), required_id_delta); + } imquic_moq_namespace tns[32]; memset(&tns, 0, sizeof(tns)); uint64_t tns_num = 0, i = 0; IMQUIC_MOQ_PARSE_NAMESPACES(IMQUIC_MOQ_PUBLISH, tns_num, i, "Broken PUBLISH", FALSE); imquic_moq_name tn = { 0 }; IMQUIC_MOQ_PARSE_TRACKNAME("Broken PUBLISH", FALSE); - uint64_t track_alias = imquic_read_varint(&bytes[offset], blen-offset, &length); + uint64_t track_alias = imquic_read_moqint(moq->version, &bytes[offset], blen-offset, &length); IMQUIC_MOQ_CHECK_ERR(length == 0 || length >= blen-offset, NULL, 0, 0, "Broken PUBLISH"); offset += length; IMQUIC_LOG(IMQUIC_MOQ_LOG_HUGE, "[%s][MoQ] -- Track Alias: %"SCNu64"\n", imquic_get_connection_name(moq->conn), track_alias); - /* For versions older than v15, we need to parse some attributes manually, - * but we'll add them to the parameters object for the application anyway */ imquic_moq_request_parameters parameters; imquic_moq_request_parameters_init_defaults(¶meters); - if(moq->version <= IMQUIC_MOQ_VERSION_14) { - uint8_t group_order = bytes[offset]; - offset++; - IMQUIC_MOQ_CHECK_ERR(group_order > IMQUIC_MOQ_ORDERING_DESCENDING, error, IMQUIC_MOQ_PROTOCOL_VIOLATION, 0, "Invalid Group Order value"); - IMQUIC_MOQ_CHECK_ERR(blen-offset == 0, NULL, 0, 0, "Broken PUBLISH"); - IMQUIC_LOG(IMQUIC_MOQ_LOG_HUGE, "[%s][MoQ] -- -- Group Order: %"SCNu8" (%s)\n", - imquic_get_connection_name(moq->conn), group_order, imquic_moq_group_order_str(group_order)); - parameters.group_order_set = TRUE; - parameters.group_order = group_order; - uint8_t content_exists = bytes[offset]; - offset++; - IMQUIC_MOQ_CHECK_ERR(content_exists > 1, error, IMQUIC_MOQ_PROTOCOL_VIOLATION, 0, "Invalid Content Exists value"); - IMQUIC_MOQ_CHECK_ERR(content_exists && blen-offset == 0, NULL, 0, 0, "Broken PUBLISH"); - IMQUIC_LOG(IMQUIC_MOQ_LOG_HUGE, "[%s][MoQ] -- Content Exists: %"SCNu8"\n", - imquic_get_connection_name(moq->conn), content_exists); - if(content_exists > 0) { - parameters.largest_object_set = TRUE; - parameters.largest_object.group = imquic_read_varint(&bytes[offset], blen-offset, &length); - IMQUIC_MOQ_CHECK_ERR(length == 0 || length >= blen-offset, NULL, 0, 0, "Broken PUBLISH"); - offset += length; - IMQUIC_LOG(IMQUIC_MOQ_LOG_HUGE, "[%s][MoQ] -- Largest Group ID: %"SCNu64"\n", - imquic_get_connection_name(moq->conn), parameters.largest_object.group); - parameters.largest_object.object = imquic_read_varint(&bytes[offset], blen-offset, &length); - IMQUIC_MOQ_CHECK_ERR(length == 0 || length > blen-offset, NULL, 0, 0, "Broken PUBLISH"); - offset += length; - IMQUIC_LOG(IMQUIC_MOQ_LOG_HUGE, "[%s][MoQ] -- Largest Object ID: %"SCNu64"\n", - imquic_get_connection_name(moq->conn), parameters.largest_object.object); - } - uint8_t forward = bytes[offset]; - IMQUIC_MOQ_CHECK_ERR(forward > 1, error, IMQUIC_MOQ_PROTOCOL_VIOLATION, 0, "Invalid Forward value"); - parameters.forward_set = TRUE; - parameters.forward = (forward > 0); - offset++; - IMQUIC_MOQ_CHECK_ERR(blen-offset == 0, NULL, 0, 0, "Broken PUBLISH"); - IMQUIC_LOG(IMQUIC_MOQ_LOG_HUGE, "[%s][MoQ] -- -- Forward: %"SCNu8")\n", - imquic_get_connection_name(moq->conn), parameters.forward); - IMQUIC_MOQ_CHECK_ERR(blen-offset == 0, NULL, 0, 0, "Broken PUBLISH"); - } - uint64_t params_num = imquic_read_varint(&bytes[offset], blen-offset, &length); + uint64_t params_num = imquic_read_moqint(moq->version, &bytes[offset], blen-offset, &length); IMQUIC_MOQ_CHECK_ERR(params_num > 0 && (length == 0 || length >= blen-offset), NULL, 0, 0, "Broken PUBLISH"); IMQUIC_MOQ_CHECK_ERR(params_num == 0 && (length == 0 || length > blen-offset), NULL, 0, 0, "Broken PUBLISH"); offset += length; @@ -2672,134 +2541,88 @@ size_t imquic_moq_parse_publish(imquic_moq_context *moq, uint8_t *bytes, size_t for(i = 0; iversion >= IMQUIC_MOQ_VERSION_16) { - /* The message contains extensions */ - ext_len = imquic_read_varint(&bytes[offset], blen-offset, &length); - IMQUIC_MOQ_CHECK_ERR(length == 0 || (ext_len > 0 && length >= blen-offset), NULL, 0, 0, "Broken PUBLISH"); - offset += length; - IMQUIC_LOG(IMQUIC_MOQ_LOG_HUGE, "[%s][MoQ] -- Track Extensions Length: %"SCNu64"\n", - imquic_get_connection_name(moq->conn), ext_len); - ext_offset = offset; - IMQUIC_MOQ_CHECK_ERR(ext_len > blen-offset, NULL, 0, 0, "Broken PUBLISH"); - offset += ext_len; - } - GList *track_extensions = NULL; - if(ext_offset > 0 && ext_len > 0) { + IMQUIC_MOQ_CHECK_ERR(error && *error, error, IMQUIC_MOQ_PROTOCOL_VIOLATION, 0, "Error parsing PUBLISH parameters"); + } + size_t prop_offset = 0, prop_len = 0; + prop_len = imquic_read_moqint(moq->version, &bytes[offset], blen-offset, &length); + IMQUIC_MOQ_CHECK_ERR(length == 0 || (prop_len > 0 && length >= blen-offset), NULL, 0, 0, "Broken PUBLISH"); + offset += length; + IMQUIC_LOG(IMQUIC_MOQ_LOG_HUGE, "[%s][MoQ] -- Track Properties Length: %"SCNu64"\n", + imquic_get_connection_name(moq->conn), prop_len); + prop_offset = offset; + IMQUIC_MOQ_CHECK_ERR(prop_len > blen-offset, NULL, 0, 0, "Broken PUBLISH"); + offset += prop_len; + GList *track_properties = NULL; + if(prop_offset > 0 && prop_len > 0) { /* TODO Check Protocol Violation cases */ - track_extensions = imquic_moq_parse_object_extensions(moq->version, &bytes[ext_offset], ext_len); + track_properties = imquic_moq_parse_properties(moq->version, &bytes[prop_offset], prop_len); } #ifdef HAVE_QLOG if(moq->conn->qlog != NULL && moq->conn->qlog->moq) { + if(moq_stream != NULL) + imquic_moq_qlog_stream_type_set(moq->conn->qlog, FALSE, moq_stream->stream_id, "publish"); json_t *message = imquic_qlog_moq_message_prepare("publish"); json_object_set_new(message, "request_id", json_integer(request_id)); + if(moq->version >= IMQUIC_MOQ_VERSION_17) + json_object_set_new(message, "required_request_id_delta", json_integer(required_id_delta)); imquic_qlog_moq_message_add_namespace(message, &tns[0], "track_namespace"); imquic_qlog_moq_message_add_track(message, &tn); json_object_set_new(message, "track_alias", json_integer(track_alias)); - if(moq->version <= IMQUIC_MOQ_VERSION_14) { - json_object_set_new(message, "group_order", json_integer(parameters.group_order)); - json_object_set_new(message, "content_exists", json_integer(parameters.largest_object_set)); - if(parameters.largest_object_set) { - json_object_set_new(message, "largest_group_id", json_integer(parameters.largest_object.group)); - json_object_set_new(message, "largest_object_id", json_integer(parameters.largest_object.object)); - } - json_object_set_new(message, "forward", json_integer(parameters.forward)); - } json_object_set_new(message, "number_of_parameters", json_integer(params_num)); imquic_qlog_moq_message_add_request_parameters(message, moq->version, ¶meters, "parameters"); - if(moq->version >= IMQUIC_MOQ_VERSION_16) - imquic_qlog_moq_message_add_extensions(message, track_extensions, "track_extensions"); - imquic_moq_qlog_control_message_parsed(moq->conn->qlog, moq->control_stream_id, bytes-3, offset+3, message); + imquic_qlog_moq_message_add_properties(message, track_properties, "track_properties"); + imquic_moq_qlog_control_message_parsed(moq->conn->qlog, + (moq_stream ? moq_stream->stream_id : imquic_moq_get_control_stream(moq)), bytes-3, offset+3, message); } #endif /* Make sure this is in line with the expected request ID */ IMQUIC_MOQ_CHECK_ERR(!moq_is_request_id_valid(moq, request_id, FALSE), error, IMQUIC_MOQ_INVALID_REQUEST_ID, 0, "Invalid Request ID"); moq->expected_request_id = request_id + IMQUIC_MOQ_REQUEST_ID_INCREMENT; + /* If we're on a recent version of MoQ, track this request via its ID */ + if(moq_stream != NULL) { + moq_stream->request_id = request_id; + moq_stream->request_state = IMQUIC_MOQ_REQUEST_STATE_SENT; + imquic_mutex_lock(&moq->mutex); + g_hash_table_insert(moq->streams_by_reqid, imquic_dup_uint64(request_id), moq_stream); + imquic_mutex_unlock(&moq->mutex); + } /* Notify the application */ if(moq->conn->socket && moq->conn->socket->callbacks.moq.incoming_publish) { moq->conn->socket->callbacks.moq.incoming_publish(moq->conn, - request_id, &tns[0], &tn, track_alias, ¶meters, track_extensions); + request_id, required_id_delta, &tns[0], &tn, track_alias, ¶meters, track_properties); } else { /* No handler for this request, let's reject it ourselves */ imquic_moq_reject_publish(moq->conn, request_id, IMQUIC_MOQ_REQERR_NOT_SUPPORTED, "Not handled", 0); } - g_list_free_full(track_extensions, (GDestroyNotify)imquic_moq_object_extension_free); + g_list_free_full(track_properties, (GDestroyNotify)imquic_moq_property_free); if(error) *error = 0; return offset; } -size_t imquic_moq_parse_publish_ok(imquic_moq_context *moq, uint8_t *bytes, size_t blen, uint8_t *error) { +size_t imquic_moq_parse_publish_ok(imquic_moq_context *moq, imquic_moq_stream *moq_stream, uint8_t *bytes, size_t blen, uint8_t *error) { if(error) *error = IMQUIC_MOQ_UNKNOWN_ERROR; if(bytes == NULL || blen < 1) return 0; + IMQUIC_MOQ_CHECK_ERR((moq->version >= IMQUIC_MOQ_VERSION_17 && (moq_stream == NULL || moq_stream->request_type != IMQUIC_MOQ_PUBLISH || + !moq_stream->request_sender || moq_stream->request_state != IMQUIC_MOQ_REQUEST_STATE_SENT)), + error, IMQUIC_MOQ_PROTOCOL_VIOLATION, 0, "Invalid use of PUBLISH_OK on bidirectional request"); size_t offset = 0; uint8_t length = 0; - uint64_t request_id = imquic_read_varint(&bytes[offset], blen-offset, &length); - IMQUIC_MOQ_CHECK_ERR(length == 0 || length >= blen-offset, NULL, 0, 0, "Broken PUBLISH_OK"); - offset += length; - IMQUIC_LOG(IMQUIC_MOQ_LOG_HUGE, "[%s][MoQ] -- Request ID: %"SCNu64"\n", - imquic_get_connection_name(moq->conn), request_id); - /* For versions older than v15, we need to parse some attributes manually, - * but we'll add them to the parameters object for the application anyway */ - imquic_moq_request_parameters parameters; - imquic_moq_request_parameters_init_defaults(¶meters); - if(moq->version <= IMQUIC_MOQ_VERSION_14) { - uint8_t forward = bytes[offset]; - IMQUIC_MOQ_CHECK_ERR(forward > 1, error, IMQUIC_MOQ_PROTOCOL_VIOLATION, 0, "Invalid Forward value"); - parameters.forward_set = TRUE; - parameters.forward = (forward > 0); - offset++; - IMQUIC_MOQ_CHECK_ERR(blen-offset == 0, NULL, 0, 0, "Broken PUBLISH_OK"); - IMQUIC_LOG(IMQUIC_MOQ_LOG_HUGE, "[%s][MoQ] -- -- Forward: %"SCNu8")\n", - imquic_get_connection_name(moq->conn), parameters.forward); - parameters.subscriber_priority_set = TRUE; - parameters.subscriber_priority = bytes[offset]; - offset++; - IMQUIC_MOQ_CHECK_ERR(blen-offset == 0, NULL, 0, 0, "Broken PUBLISH_OK"); - IMQUIC_LOG(IMQUIC_MOQ_LOG_HUGE, "[%s][MoQ] -- -- Subscriber Priority: %"SCNu8")\n", - imquic_get_connection_name(moq->conn), parameters.subscriber_priority); - uint8_t group_order = bytes[offset]; - offset++; - IMQUIC_MOQ_CHECK_ERR((group_order > IMQUIC_MOQ_ORDERING_DESCENDING), error, IMQUIC_MOQ_PROTOCOL_VIOLATION, 0, "Invalid Group Order value"); - IMQUIC_MOQ_CHECK_ERR(blen-offset == 0, NULL, 0, 0, "Broken PUBLISH_OK"); - IMQUIC_LOG(IMQUIC_MOQ_LOG_HUGE, "[%s][MoQ] -- -- Group Order: %"SCNu8" (%s)\n", - imquic_get_connection_name(moq->conn), group_order, imquic_moq_group_order_str(group_order)); - parameters.group_order_set = TRUE; - parameters.group_order = group_order; - uint64_t filter = imquic_read_varint(&bytes[offset], blen-offset, &length); - IMQUIC_MOQ_CHECK_ERR(length == 0 || length >= blen-offset, NULL, 0, 0, "Broken PUBLISH_OK"); - IMQUIC_MOQ_CHECK_ERR((filter < 0x1 || filter > 0x4), error, IMQUIC_MOQ_PROTOCOL_VIOLATION, 0, "Invalid Filter type"); + uint64_t request_id = 0; + if(moq->version <= IMQUIC_MOQ_VERSION_16) { + request_id = imquic_read_moqint(moq->version, &bytes[offset], blen-offset, &length); + IMQUIC_MOQ_CHECK_ERR(length == 0 || length > blen-offset, NULL, 0, 0, "Broken REQUEST_OK"); offset += length; - IMQUIC_LOG(IMQUIC_MOQ_LOG_HUGE, "[%s][MoQ] -- Filter type: %s (%"SCNu64")\n", - imquic_get_connection_name(moq->conn), imquic_moq_filter_type_str(filter), filter); - parameters.subscription_filter_set = TRUE; - parameters.subscription_filter.type = filter; - if(filter == IMQUIC_MOQ_FILTER_ABSOLUTE_START || filter == IMQUIC_MOQ_FILTER_ABSOLUTE_RANGE) { - parameters.subscription_filter.start_location.group = imquic_read_varint(&bytes[offset], blen-offset, &length); - IMQUIC_MOQ_CHECK_ERR(length == 0 || length >= blen-offset, NULL, 0, 0, "Broken PUBLISH_OK"); - offset += length; - IMQUIC_LOG(IMQUIC_MOQ_LOG_HUGE, "[%s][MoQ] -- -- Start Group: %"SCNu64")\n", - imquic_get_connection_name(moq->conn), parameters.subscription_filter.start_location.group); - parameters.subscription_filter.start_location.object = imquic_read_varint(&bytes[offset], blen-offset, &length); - IMQUIC_MOQ_CHECK_ERR(length == 0 || length >= blen-offset, NULL, 0, 0, "Broken PUBLISH_OK"); - offset += length; - IMQUIC_LOG(IMQUIC_MOQ_LOG_HUGE, "[%s][MoQ] -- -- Start Object: %"SCNu64")\n", - imquic_get_connection_name(moq->conn), parameters.subscription_filter.start_location.object); - } - if(filter == IMQUIC_MOQ_FILTER_ABSOLUTE_RANGE) { - parameters.subscription_filter.end_group = imquic_read_varint(&bytes[offset], blen-offset, &length); - IMQUIC_MOQ_CHECK_ERR(length == 0 || length >= blen-offset, NULL, 0, 0, "Broken PUBLISH_OK"); - offset += length; - IMQUIC_LOG(IMQUIC_MOQ_LOG_HUGE, "[%s][MoQ] -- -- End Group: %"SCNu64")\n", - imquic_get_connection_name(moq->conn), parameters.subscription_filter.end_group); - } + IMQUIC_LOG(IMQUIC_MOQ_LOG_HUGE, "[%s][MoQ] -- Request ID: %"SCNu64"\n", + imquic_get_connection_name(moq->conn), request_id); + } else { + request_id = moq_stream->request_id; } - uint64_t params_num = imquic_read_varint(&bytes[offset], blen-offset, &length); + imquic_moq_request_parameters parameters; + imquic_moq_request_parameters_init_defaults(¶meters); + uint64_t params_num = imquic_read_moqint(moq->version, &bytes[offset], blen-offset, &length); IMQUIC_MOQ_CHECK_ERR(params_num > 0 && (length == 0 || length >= blen-offset), NULL, 0, 0, "Broken PUBLISH_OK"); IMQUIC_MOQ_CHECK_ERR(params_num == 0 && (length == 0 || length > blen-offset), NULL, 0, 0, "Broken PUBLISH_OK"); offset += length; @@ -2809,29 +2632,21 @@ size_t imquic_moq_parse_publish_ok(imquic_moq_context *moq, uint8_t *bytes, size for(i = 0; iconn->qlog != NULL && moq->conn->qlog->moq) { json_t *message = imquic_qlog_moq_message_prepare("publish_ok"); - json_object_set_new(message, "request_id", json_integer(request_id)); - if(moq->version <= IMQUIC_MOQ_VERSION_14) { - json_object_set_new(message, "forward", json_integer(parameters.forward)); - json_object_set_new(message, "subscriber_priority", json_integer(parameters.subscriber_priority)); - json_object_set_new(message, "group_order", json_integer(parameters.group_order)); - json_object_set_new(message, "filter_type", json_integer(parameters.subscription_filter.type)); - if(parameters.subscription_filter.type == IMQUIC_MOQ_FILTER_ABSOLUTE_START || parameters.subscription_filter.type == IMQUIC_MOQ_FILTER_ABSOLUTE_RANGE) { - json_object_set_new(message, "start_group", json_integer(parameters.subscription_filter.start_location.group)); - json_object_set_new(message, "start_object", json_integer(parameters.subscription_filter.start_location.object)); - } - if(parameters.subscription_filter.type == IMQUIC_MOQ_FILTER_ABSOLUTE_RANGE) - json_object_set_new(message, "end_group", json_integer(parameters.subscription_filter.end_group)); - } + if(moq->version <= IMQUIC_MOQ_VERSION_16) + json_object_set_new(message, "request_id", json_integer(request_id)); json_object_set_new(message, "number_of_parameters", json_integer(params_num)); imquic_qlog_moq_message_add_request_parameters(message, moq->version, ¶meters, "parameters"); - imquic_moq_qlog_control_message_parsed(moq->conn->qlog, moq->control_stream_id, bytes-3, offset+3, message); + imquic_moq_qlog_control_message_parsed(moq->conn->qlog, + (moq_stream ? moq_stream->stream_id : imquic_moq_get_control_stream(moq)), bytes-3, offset+3, message); } #endif + if(moq_stream != NULL) + moq_stream->request_state = IMQUIC_MOQ_REQUEST_STATE_OK; /* Notify the application */ if(moq->conn->socket && moq->conn->socket->callbacks.moq.publish_accepted) moq->conn->socket->callbacks.moq.publish_accepted(moq->conn, request_id, ¶meters); @@ -2840,143 +2655,39 @@ size_t imquic_moq_parse_publish_ok(imquic_moq_context *moq, uint8_t *bytes, size return offset; } -size_t imquic_moq_parse_publish_error(imquic_moq_context *moq, uint8_t *bytes, size_t blen, uint8_t *error) { - if(error) - *error = IMQUIC_MOQ_UNKNOWN_ERROR; - if(bytes == NULL || blen < 1) - return 0; - size_t offset = 0; - uint8_t length = 0; - uint64_t request_id = imquic_read_varint(&bytes[offset], blen-offset, &length); - IMQUIC_MOQ_CHECK_ERR(length == 0 || length >= blen-offset, NULL, 0, 0, "Broken PUBLISH_ERROR"); - offset += length; - IMQUIC_LOG(IMQUIC_MOQ_LOG_HUGE, "[%s][MoQ] -- Request ID: %"SCNu64"\n", - imquic_get_connection_name(moq->conn), request_id); - uint64_t error_code = imquic_read_varint(&bytes[offset], blen-offset, &length); - IMQUIC_MOQ_CHECK_ERR(length == 0 || length >= blen-offset, NULL, 0, 0, "Broken PUBLISH_ERROR"); - offset += length; - uint64_t recvd_error_code = error_code; - if(moq->version < IMQUIC_MOQ_VERSION_15) - error_code = imquic_moq_request_error_code_from_legacy(moq->version, error_code); - IMQUIC_LOG(IMQUIC_MOQ_LOG_HUGE, "[%s][MoQ] -- Error Code: %s (%"SCNu64")\n", - imquic_get_connection_name(moq->conn), imquic_moq_request_error_code_str(error_code), recvd_error_code); - uint64_t rs_len = imquic_read_varint(&bytes[offset], blen-offset, &length); - IMQUIC_MOQ_CHECK_ERR(length == 0 || length > blen-offset, NULL, 0, 0, "Broken PUBLISH_ERROR"); - offset += length; - char reason[1024], *reason_str = NULL; - if(rs_len > 0) { - IMQUIC_MOQ_CHECK_ERR(rs_len > blen-offset, NULL, 0, 0, "Broken PUBLISH_ERROR"); - IMQUIC_MOQ_CHECK_ERR(rs_len > sizeof(reason), error, IMQUIC_MOQ_PROTOCOL_VIOLATION, 0, "Invalid reason length"); - int reason_len = (int)rs_len; - IMQUIC_LOG(IMQUIC_MOQ_LOG_HUGE, "[%s][MoQ] -- Reason Phrase: %.*s\n", - imquic_get_connection_name(moq->conn), reason_len, &bytes[offset]); - if(reason_len > 0) { - g_snprintf(reason, sizeof(reason), "%.*s", reason_len, &bytes[offset]); - reason_str = reason; - } - offset += reason_len; - } -#ifdef HAVE_QLOG - if(moq->conn->qlog != NULL && moq->conn->qlog->moq) { - json_t *message = imquic_qlog_moq_message_prepare(moq->version >= IMQUIC_MOQ_VERSION_15 ? "request_error" : "publish_error"); - json_object_set_new(message, "request_id", json_integer(request_id)); - json_object_set_new(message, "error_code", json_integer(recvd_error_code)); - if(reason_str != NULL) - json_object_set_new(message, "reason", json_string(reason_str)); - imquic_moq_qlog_control_message_parsed(moq->conn->qlog, moq->control_stream_id, bytes-3, offset+3, message); - } -#endif - /* Notify the application */ - if(moq->conn->socket && moq->conn->socket->callbacks.moq.publish_error) - moq->conn->socket->callbacks.moq.publish_error(moq->conn, request_id, error_code, reason_str, 0); - if(error) - *error = 0; - return offset; -} - -size_t imquic_moq_parse_subscribe(imquic_moq_context *moq, uint8_t *bytes, size_t blen, uint8_t *error) { +size_t imquic_moq_parse_subscribe(imquic_moq_context *moq, imquic_moq_stream *moq_stream, uint8_t *bytes, size_t blen, uint8_t *error) { if(error) *error = IMQUIC_MOQ_UNKNOWN_ERROR; if(bytes == NULL || blen < 1) return 0; + IMQUIC_MOQ_CHECK_ERR((moq->version >= IMQUIC_MOQ_VERSION_17 && (moq_stream == NULL || + moq_stream->request_sender || moq_stream->request_state != IMQUIC_MOQ_REQUEST_STATE_NEW)), + error, IMQUIC_MOQ_PROTOCOL_VIOLATION, 0, "Invalid use of SUBSCRIBE on bidirectional request"); size_t offset = 0; uint8_t length = 0; - uint64_t request_id = imquic_read_varint(&bytes[offset], blen-offset, &length); + uint64_t request_id = imquic_read_moqint(moq->version, &bytes[offset], blen-offset, &length); IMQUIC_MOQ_CHECK_ERR(length == 0 || length >= blen-offset, NULL, 0, 0, "Broken SUBSCRIBE"); offset += length; IMQUIC_LOG(IMQUIC_MOQ_LOG_HUGE, "[%s][MoQ] -- Request ID: %"SCNu64"\n", imquic_get_connection_name(moq->conn), request_id); - /* Move on */ - uint64_t track_alias = 0; - if(moq->version < IMQUIC_MOQ_VERSION_12) { - track_alias = imquic_read_varint(&bytes[offset], blen-offset, &length); + uint64_t required_id_delta = 0; + if(moq->version >= IMQUIC_MOQ_VERSION_17) { + required_id_delta = imquic_read_moqint(moq->version, &bytes[offset], blen-offset, &length); IMQUIC_MOQ_CHECK_ERR(length == 0 || length >= blen-offset, NULL, 0, 0, "Broken SUBSCRIBE"); offset += length; - IMQUIC_LOG(IMQUIC_MOQ_LOG_HUGE, "[%s][MoQ] -- Track Alias: %"SCNu64"\n", - imquic_get_connection_name(moq->conn), track_alias); + IMQUIC_LOG(IMQUIC_MOQ_LOG_HUGE, "[%s][MoQ] -- Required Request ID Delta: %"SCNu64"\n", + imquic_get_connection_name(moq->conn), required_id_delta); } + /* Move on */ imquic_moq_namespace tns[32]; memset(&tns, 0, sizeof(tns)); uint64_t tns_num = 0, i = 0; IMQUIC_MOQ_PARSE_NAMESPACES(IMQUIC_MOQ_SUBSCRIBE, tns_num, i, "Broken SUBSCRIBE", FALSE); imquic_moq_name tn = { 0 }; IMQUIC_MOQ_PARSE_TRACKNAME("Broken SUBSCRIBE", FALSE); - /* For versions older than v15, we need to parse some attributes manually, - * but we'll add them to the parameters object for the application anyway */ imquic_moq_request_parameters parameters; imquic_moq_request_parameters_init_defaults(¶meters); - if(moq->version <= IMQUIC_MOQ_VERSION_14) { - parameters.subscriber_priority_set = TRUE; - parameters.subscriber_priority = bytes[offset]; - offset++; - IMQUIC_MOQ_CHECK_ERR(blen-offset == 0, NULL, 0, 0, "Broken SUBSCRIBE"); - IMQUIC_LOG(IMQUIC_MOQ_LOG_HUGE, "[%s][MoQ] -- -- Subscriber Priority: %"SCNu8")\n", - imquic_get_connection_name(moq->conn), parameters.subscriber_priority); - uint8_t group_order = bytes[offset]; - offset++; - IMQUIC_MOQ_CHECK_ERR((group_order > IMQUIC_MOQ_ORDERING_DESCENDING), error, IMQUIC_MOQ_PROTOCOL_VIOLATION, 0, "Invalid Group Order value"); - IMQUIC_MOQ_CHECK_ERR(blen-offset == 0, NULL, 0, 0, "Broken SUBSCRIBE"); - IMQUIC_LOG(IMQUIC_MOQ_LOG_HUGE, "[%s][MoQ] -- -- Group Order: %"SCNu8" (%s)\n", - imquic_get_connection_name(moq->conn), group_order, imquic_moq_group_order_str(group_order)); - parameters.group_order_set = TRUE; - parameters.group_order = group_order; - uint8_t forward = bytes[offset]; - IMQUIC_MOQ_CHECK_ERR(forward > 1, error, IMQUIC_MOQ_PROTOCOL_VIOLATION, 0, "Invalid Forward value"); - parameters.forward_set = TRUE; - parameters.forward = (forward > 0); - offset++; - IMQUIC_MOQ_CHECK_ERR(blen-offset == 0, NULL, 0, 0, "Broken SUBSCRIBE"); - IMQUIC_LOG(IMQUIC_MOQ_LOG_HUGE, "[%s][MoQ] -- -- Forward: %"SCNu8")\n", - imquic_get_connection_name(moq->conn), forward); - uint64_t filter = imquic_read_varint(&bytes[offset], blen-offset, &length); - IMQUIC_MOQ_CHECK_ERR(length == 0 || length >= blen-offset, NULL, 0, 0, "Broken SUBSCRIBE"); - IMQUIC_MOQ_CHECK_ERR((filter < 0x1 || filter > 0x4), error, IMQUIC_MOQ_PROTOCOL_VIOLATION, 0, "Invalid Filter type"); - offset += length; - IMQUIC_LOG(IMQUIC_MOQ_LOG_HUGE, "[%s][MoQ] -- Filter type: %s (%"SCNu64")\n", - imquic_get_connection_name(moq->conn), imquic_moq_filter_type_str(filter), filter); - parameters.subscription_filter_set = TRUE; - parameters.subscription_filter.type = filter; - if(filter == IMQUIC_MOQ_FILTER_ABSOLUTE_START || filter == IMQUIC_MOQ_FILTER_ABSOLUTE_RANGE) { - parameters.subscription_filter.start_location.group = imquic_read_varint(&bytes[offset], blen-offset, &length); - IMQUIC_MOQ_CHECK_ERR(length == 0 || length >= blen-offset, NULL, 0, 0, "Broken SUBSCRIBE"); - offset += length; - IMQUIC_LOG(IMQUIC_MOQ_LOG_HUGE, "[%s][MoQ] -- -- Start Group: %"SCNu64")\n", - imquic_get_connection_name(moq->conn), parameters.subscription_filter.start_location.group); - parameters.subscription_filter.start_location.object = imquic_read_varint(&bytes[offset], blen-offset, &length); - IMQUIC_MOQ_CHECK_ERR(length == 0 || length >= blen-offset, NULL, 0, 0, "Broken SUBSCRIBE"); - offset += length; - IMQUIC_LOG(IMQUIC_MOQ_LOG_HUGE, "[%s][MoQ] -- -- Start Object: %"SCNu64")\n", - imquic_get_connection_name(moq->conn), parameters.subscription_filter.start_location.object); - } - if(filter == IMQUIC_MOQ_FILTER_ABSOLUTE_RANGE) { - parameters.subscription_filter.end_group = imquic_read_varint(&bytes[offset], blen-offset, &length); - IMQUIC_MOQ_CHECK_ERR(length == 0 || length >= blen-offset, NULL, 0, 0, "Broken SUBSCRIBE"); - offset += length; - IMQUIC_LOG(IMQUIC_MOQ_LOG_HUGE, "[%s][MoQ] -- -- End Group: %"SCNu64")\n", - imquic_get_connection_name(moq->conn), parameters.subscription_filter.end_group); - } - } - uint64_t params_num = imquic_read_varint(&bytes[offset], blen-offset, &length); + uint64_t params_num = imquic_read_moqint(moq->version, &bytes[offset], blen-offset, &length); IMQUIC_MOQ_CHECK_ERR(params_num > 0 && (length == 0 || length >= blen-offset), NULL, 0, 0, "Broken SUBSCRIBE"); IMQUIC_MOQ_CHECK_ERR(params_num == 0 && (length == 0 || length > blen-offset), NULL, 0, 0, "Broken SUBSCRIBE"); offset += length; @@ -2986,114 +2697,86 @@ size_t imquic_moq_parse_subscribe(imquic_moq_context *moq, uint8_t *bytes, size_ for(i = 0; iconn->qlog != NULL && moq->conn->qlog->moq) { + if(moq_stream != NULL) + imquic_moq_qlog_stream_type_set(moq->conn->qlog, FALSE, moq_stream->stream_id, "subscribe"); json_t *message = imquic_qlog_moq_message_prepare("subscribe"); json_object_set_new(message, "request_id", json_integer(request_id)); - if(moq->version < IMQUIC_MOQ_VERSION_12) - json_object_set_new(message, "track_alias", json_integer(track_alias)); + if(moq->version >= IMQUIC_MOQ_VERSION_17) + json_object_set_new(message, "required_request_id_delta", json_integer(required_id_delta)); imquic_qlog_moq_message_add_namespace(message, &tns[0], "track_namespace"); imquic_qlog_moq_message_add_track(message, &tn); - if(moq->version <= IMQUIC_MOQ_VERSION_14) { - json_object_set_new(message, "subscriber_priority", json_integer(parameters.subscriber_priority)); - json_object_set_new(message, "group_order", json_integer(parameters.group_order)); - json_object_set_new(message, "forward", json_integer(parameters.forward)); - json_object_set_new(message, "filter_type", json_integer(parameters.subscription_filter.type)); - if(parameters.subscription_filter.type == IMQUIC_MOQ_FILTER_ABSOLUTE_START || parameters.subscription_filter.type == IMQUIC_MOQ_FILTER_ABSOLUTE_RANGE) { - json_object_set_new(message, "start_group", json_integer(parameters.subscription_filter.start_location.group)); - json_object_set_new(message, "start_object", json_integer(parameters.subscription_filter.start_location.object)); - } - if(parameters.subscription_filter.type == IMQUIC_MOQ_FILTER_ABSOLUTE_RANGE) - json_object_set_new(message, "end_group", json_integer(parameters.subscription_filter.end_group)); - } json_object_set_new(message, "number_of_parameters", json_integer(params_num)); imquic_qlog_moq_message_add_request_parameters(message, moq->version, ¶meters, "parameters"); - imquic_moq_qlog_control_message_parsed(moq->conn->qlog, moq->control_stream_id, bytes-3, offset+3, message); + imquic_moq_qlog_control_message_parsed(moq->conn->qlog, + (moq_stream ? moq_stream->stream_id : imquic_moq_get_control_stream(moq)), bytes-3, offset+3, message); } #endif /* Make sure this is in line with the expected request ID */ IMQUIC_MOQ_CHECK_ERR(!moq_is_request_id_valid(moq, request_id, FALSE), error, IMQUIC_MOQ_INVALID_REQUEST_ID, 0, "Invalid Request ID"); moq->expected_request_id = request_id + IMQUIC_MOQ_REQUEST_ID_INCREMENT; + /* If we're on a recent version of MoQ, track this request via its ID */ + if(moq_stream != NULL) { + moq_stream->request_id = request_id; + moq_stream->request_state = IMQUIC_MOQ_REQUEST_STATE_SENT; + imquic_mutex_lock(&moq->mutex); + g_hash_table_insert(moq->streams_by_reqid, imquic_dup_uint64(request_id), moq_stream); + imquic_mutex_unlock(&moq->mutex); + } /* Track this subscription */ - imquic_moq_subscription *moq_sub = imquic_moq_subscription_create(request_id, track_alias); + imquic_moq_subscription *moq_sub = imquic_moq_subscription_create(request_id, 0); imquic_mutex_lock(&moq->mutex); g_hash_table_insert(moq->subscriptions_by_id, imquic_dup_uint64(request_id), moq_sub); - if(moq->version < IMQUIC_MOQ_VERSION_12) - g_hash_table_insert(moq->subscriptions, imquic_dup_uint64(track_alias), moq_sub); imquic_mutex_unlock(&moq->mutex); /* Notify the application */ if(moq->conn->socket && moq->conn->socket->callbacks.moq.incoming_subscribe) { moq->conn->socket->callbacks.moq.incoming_subscribe(moq->conn, - request_id, track_alias, &tns[0], &tn, ¶meters); + request_id, required_id_delta, &tns[0], &tn, ¶meters); } else { /* No handler for this request, let's reject it ourselves */ - imquic_moq_reject_subscribe(moq->conn, request_id, IMQUIC_MOQ_REQERR_NOT_SUPPORTED, "Not handled", track_alias, 0); + imquic_moq_reject_subscribe(moq->conn, request_id, IMQUIC_MOQ_REQERR_NOT_SUPPORTED, "Not handled", 0); } if(error) *error = 0; return offset; } -size_t imquic_moq_parse_request_update(imquic_moq_context *moq, uint8_t *bytes, size_t blen, uint8_t *error) { +size_t imquic_moq_parse_request_update(imquic_moq_context *moq, imquic_moq_stream *moq_stream, uint8_t *bytes, size_t blen, uint8_t *error) { if(error) *error = IMQUIC_MOQ_UNKNOWN_ERROR; if(bytes == NULL || blen < 1) return 0; + /* FIXME State management needs to be fixed, because an update will trigger OK/ERROR too */ + IMQUIC_MOQ_CHECK_ERR((moq->version >= IMQUIC_MOQ_VERSION_17 && (moq_stream == NULL || moq_stream->request_type == 0 || + moq_stream->request_sender || moq_stream->request_state != IMQUIC_MOQ_REQUEST_STATE_OK)), + error, IMQUIC_MOQ_PROTOCOL_VIOLATION, 0, "Invalid use of REQUEST_UPDATE on bidirectional request"); size_t offset = 0; uint8_t length = 0; - uint64_t request_id = imquic_read_varint(&bytes[offset], blen-offset, &length); + uint64_t request_id = imquic_read_moqint(moq->version, &bytes[offset], blen-offset, &length); IMQUIC_MOQ_CHECK_ERR(length == 0 || length >= blen-offset, NULL, 0, 0, "Broken REQUEST_UPDATE"); offset += length; IMQUIC_LOG(IMQUIC_MOQ_LOG_HUGE, "[%s][MoQ] -- Request ID: %"SCNu64"\n", imquic_get_connection_name(moq->conn), request_id); - uint64_t sub_request_id = 0; - if(moq->version >= IMQUIC_MOQ_VERSION_14) { - sub_request_id = imquic_read_varint(&bytes[offset], blen-offset, &length); + uint64_t sub_request_id = 0, required_id_delta = 0; + if(moq->version <= IMQUIC_MOQ_VERSION_16) { + sub_request_id = imquic_read_moqint(moq->version, &bytes[offset], blen-offset, &length); IMQUIC_MOQ_CHECK_ERR(length == 0 || length >= blen-offset, NULL, 0, 0, "Broken REQUEST_UPDATE"); offset += length; IMQUIC_LOG(IMQUIC_MOQ_LOG_HUGE, "[%s][MoQ] -- Subscription Request ID: %"SCNu64"\n", imquic_get_connection_name(moq->conn), sub_request_id); + } else { + required_id_delta = imquic_read_moqint(moq->version, &bytes[offset], blen-offset, &length); + IMQUIC_MOQ_CHECK_ERR(length == 0 || length >= blen-offset, NULL, 0, 0, "Broken PUBLISH"); + offset += length; + IMQUIC_LOG(IMQUIC_MOQ_LOG_HUGE, "[%s][MoQ] -- Required Request ID Delta: %"SCNu64"\n", + imquic_get_connection_name(moq->conn), required_id_delta); } - /* For versions older than v15, we need to parse some attributes manually, - * but we'll add them to the parameters object for the application anyway */ imquic_moq_request_parameters parameters; imquic_moq_request_parameters_init_defaults(¶meters); - if(moq->version <= IMQUIC_MOQ_VERSION_14) { - parameters.subscription_filter_set = TRUE; - parameters.subscription_filter.type = IMQUIC_MOQ_FILTER_ABSOLUTE_RANGE; /* FIXME */ - parameters.subscription_filter.start_location.group = imquic_read_varint(&bytes[offset], blen-offset, &length); - IMQUIC_MOQ_CHECK_ERR(length == 0 || length >= blen-offset, NULL, 0, 0, "Broken REQUEST_UPDATE"); - offset += length; - IMQUIC_LOG(IMQUIC_MOQ_LOG_HUGE, "[%s][MoQ] -- -- Start Group: %"SCNu64")\n", - imquic_get_connection_name(moq->conn), parameters.subscription_filter.start_location.group); - parameters.subscription_filter.start_location.object = imquic_read_varint(&bytes[offset], blen-offset, &length); - IMQUIC_MOQ_CHECK_ERR(length == 0 || length >= blen-offset, NULL, 0, 0, "Broken REQUEST_UPDATE"); - offset += length; - IMQUIC_LOG(IMQUIC_MOQ_LOG_HUGE, "[%s][MoQ] -- -- Start Object: %"SCNu64")\n", - imquic_get_connection_name(moq->conn), parameters.subscription_filter.start_location.object); - parameters.subscription_filter.end_group = imquic_read_varint(&bytes[offset], blen-offset, &length); - IMQUIC_MOQ_CHECK_ERR(length == 0 || length >= blen-offset, NULL, 0, 0, "Broken REQUEST_UPDATE"); - offset += length; - IMQUIC_LOG(IMQUIC_MOQ_LOG_HUGE, "[%s][MoQ] -- -- End Group: %"SCNu64")\n", - imquic_get_connection_name(moq->conn), parameters.subscription_filter.end_group); - parameters.subscriber_priority_set = TRUE; - parameters.subscriber_priority = bytes[offset]; - offset++; - IMQUIC_MOQ_CHECK_ERR(blen-offset == 0, NULL, 0, 0, "Broken REQUEST_UPDATE"); - IMQUIC_LOG(IMQUIC_MOQ_LOG_HUGE, "[%s][MoQ] -- -- Subscriber Priority: %"SCNu8")\n", - imquic_get_connection_name(moq->conn), parameters.subscriber_priority); - uint8_t forward = bytes[offset]; - IMQUIC_MOQ_CHECK_ERR(forward > 1, error, IMQUIC_MOQ_PROTOCOL_VIOLATION, 0, "Invalid Forward value"); - parameters.forward_set = TRUE; - parameters.forward = (forward > 0); - offset++; - IMQUIC_MOQ_CHECK_ERR(blen-offset == 0, NULL, 0, 0, "Broken REQUEST_UPDATE"); - IMQUIC_LOG(IMQUIC_MOQ_LOG_HUGE, "[%s][MoQ] -- -- Forward: %"SCNu8")\n", - imquic_get_connection_name(moq->conn), forward); - } - uint64_t params_num = imquic_read_varint(&bytes[offset], blen-offset, &length); + uint64_t params_num = imquic_read_moqint(moq->version, &bytes[offset], blen-offset, &length); IMQUIC_MOQ_CHECK_ERR(params_num > 0 && (length == 0 || length >= blen-offset), NULL, 0, 0, "Broken REQUEST_UPDATE"); IMQUIC_MOQ_CHECK_ERR(params_num == 0 && (length == 0 || length > blen-offset), NULL, 0, 0, "Broken REQUEST_UPDATE"); offset += length; @@ -3103,36 +2786,37 @@ size_t imquic_moq_parse_request_update(imquic_moq_context *moq, uint8_t *bytes, for(i = 0; iconn->qlog != NULL && moq->conn->qlog->moq) { json_t *message = imquic_qlog_moq_message_prepare("request_update"); json_object_set_new(message, "request_id", json_integer(request_id)); - if(moq->version >= IMQUIC_MOQ_VERSION_14) + if(moq->version <= IMQUIC_MOQ_VERSION_16) { json_object_set_new(message, "subscription_request_id", json_integer(sub_request_id)); - if(moq->version <= IMQUIC_MOQ_VERSION_14) { - json_object_set_new(message, "start_group", json_integer(parameters.subscription_filter.start_location.group)); - json_object_set_new(message, "start_object", json_integer(parameters.subscription_filter.start_location.object)); - json_object_set_new(message, "end_group", json_integer(parameters.subscription_filter.end_group)); - json_object_set_new(message, "subscriber_priority", json_integer(parameters.subscriber_priority)); - json_object_set_new(message, "forward", json_integer(parameters.forward)); - json_object_set_new(message, "number_of_parameters", json_integer(params_num)); + } else { + json_object_set_new(message, "required_request_id_delta", json_integer(sub_request_id)); } imquic_qlog_moq_message_add_request_parameters(message, moq->version, ¶meters, "parameters"); - imquic_moq_qlog_control_message_parsed(moq->conn->qlog, moq->control_stream_id, bytes-3, offset+3, message); + imquic_moq_qlog_control_message_parsed(moq->conn->qlog, + (moq_stream ? moq_stream->stream_id : imquic_moq_get_control_stream(moq)), bytes-3, offset+3, message); } #endif - if(moq->version >= IMQUIC_MOQ_VERSION_14) { - /* Make sure this is in line with the expected request ID */ - IMQUIC_MOQ_CHECK_ERR(!moq_is_request_id_valid(moq, request_id, FALSE), error, IMQUIC_MOQ_INVALID_REQUEST_ID, 0, "Invalid Request ID"); - moq->expected_request_id = request_id + IMQUIC_MOQ_REQUEST_ID_INCREMENT; + if(moq_stream != NULL) { + sub_request_id = moq_stream->request_id; + moq_stream->request_state = IMQUIC_MOQ_REQUEST_STATE_UPDATE_SENT; } + /* Make sure this is in line with the expected request ID */ + IMQUIC_MOQ_CHECK_ERR(!moq_is_request_id_valid(moq, request_id, FALSE), error, IMQUIC_MOQ_INVALID_REQUEST_ID, 0, "Invalid Request ID"); + moq->expected_request_id = request_id + IMQUIC_MOQ_REQUEST_ID_INCREMENT; /* Notify the application */ if(moq->conn->socket && moq->conn->socket->callbacks.moq.request_updated) { + imquic_mutex_lock(&moq->mutex); + g_hash_table_insert(moq->update_requests, imquic_dup_uint64(request_id), imquic_dup_uint64(sub_request_id)); + imquic_mutex_unlock(&moq->mutex); moq->conn->socket->callbacks.moq.request_updated(moq->conn, - request_id, sub_request_id, ¶meters); - } else if(moq->version != IMQUIC_MOQ_VERSION_15) { + request_id, sub_request_id, required_id_delta, ¶meters); + } else { /* No handler for this request, let's reject it ourselves */ imquic_moq_reject_request_update(moq->conn, request_id, IMQUIC_MOQ_REQERR_NOT_SUPPORTED, "Not handled", 0); } @@ -3141,68 +2825,34 @@ size_t imquic_moq_parse_request_update(imquic_moq_context *moq, uint8_t *bytes, return offset; } -size_t imquic_moq_parse_subscribe_ok(imquic_moq_context *moq, uint8_t *bytes, size_t blen, uint8_t *error) { +size_t imquic_moq_parse_subscribe_ok(imquic_moq_context *moq, imquic_moq_stream *moq_stream, uint8_t *bytes, size_t blen, uint8_t *error) { if(error) *error = IMQUIC_MOQ_UNKNOWN_ERROR; if(bytes == NULL || blen < 1) return 0; + IMQUIC_MOQ_CHECK_ERR((moq->version >= IMQUIC_MOQ_VERSION_17 && (moq_stream == NULL || moq_stream->request_type != IMQUIC_MOQ_SUBSCRIBE || + !moq_stream->request_sender || moq_stream->request_state != IMQUIC_MOQ_REQUEST_STATE_SENT)), + error, IMQUIC_MOQ_PROTOCOL_VIOLATION, 0, "Invalid use of SUBSCRIBE_OK on bidirectional request"); size_t offset = 0; uint8_t length = 0; - uint64_t request_id = imquic_read_varint(&bytes[offset], blen-offset, &length); - IMQUIC_MOQ_CHECK_ERR(length == 0 || length >= blen-offset, NULL, 0, 0, "Broken SUBSCRIBE_OK"); - offset += length; - IMQUIC_LOG(IMQUIC_MOQ_LOG_HUGE, "[%s][MoQ] -- Request ID: %"SCNu64"\n", - imquic_get_connection_name(moq->conn), request_id); - uint64_t track_alias = 0; - if(moq->version >= IMQUIC_MOQ_VERSION_12) { - track_alias = imquic_read_varint(&bytes[offset], blen-offset, &length); - IMQUIC_MOQ_CHECK_ERR(length == 0 || length >= blen-offset, NULL, 0, 0, "Broken SUBSCRIBE_OK"); + uint64_t request_id = 0; + if(moq->version <= IMQUIC_MOQ_VERSION_16) { + request_id = imquic_read_moqint(moq->version, &bytes[offset], blen-offset, &length); + IMQUIC_MOQ_CHECK_ERR(length == 0 || length > blen-offset, NULL, 0, 0, "Broken SUBSCRIBE_OK"); offset += length; - IMQUIC_LOG(IMQUIC_MOQ_LOG_HUGE, "[%s][MoQ] -- Track Alias: %"SCNu64"\n", - imquic_get_connection_name(moq->conn), track_alias); + IMQUIC_LOG(IMQUIC_MOQ_LOG_HUGE, "[%s][MoQ] -- Request ID: %"SCNu64"\n", + imquic_get_connection_name(moq->conn), request_id); + } else { + request_id = moq_stream->request_id; } - /* For versions older than v15, we need to parse some attributes manually, - * but we'll add them to the parameters object for the application anyway */ + uint64_t track_alias = imquic_read_moqint(moq->version, &bytes[offset], blen-offset, &length); + IMQUIC_MOQ_CHECK_ERR(length == 0 || length >= blen-offset, NULL, 0, 0, "Broken SUBSCRIBE_OK"); + offset += length; + IMQUIC_LOG(IMQUIC_MOQ_LOG_HUGE, "[%s][MoQ] -- Track Alias: %"SCNu64"\n", + imquic_get_connection_name(moq->conn), track_alias); imquic_moq_request_parameters parameters; imquic_moq_request_parameters_init_defaults(¶meters); - if(moq->version <= IMQUIC_MOQ_VERSION_14) { - parameters.expires_set = TRUE; - parameters.expires = imquic_read_varint(&bytes[offset], blen-offset, &length); - IMQUIC_MOQ_CHECK_ERR(length == 0 || length >= blen-offset, NULL, 0, 0, "Broken SUBSCRIBE_OK"); - offset += length; - IMQUIC_LOG(IMQUIC_MOQ_LOG_HUGE, "[%s][MoQ] -- Expires: %"SCNu64"\n", - imquic_get_connection_name(moq->conn), parameters.expires); - IMQUIC_MOQ_CHECK_ERR(blen-offset == 0, NULL, 0, 0, "Broken SUBSCRIBE_OK"); - uint8_t group_order = bytes[offset]; - offset++; - IMQUIC_MOQ_CHECK_ERR((group_order > IMQUIC_MOQ_ORDERING_DESCENDING), error, IMQUIC_MOQ_PROTOCOL_VIOLATION, 0, "Invalid Group Order value"); - IMQUIC_MOQ_CHECK_ERR(blen-offset == 0, NULL, 0, 0, "Broken SUBSCRIBE_OK"); - IMQUIC_LOG(IMQUIC_MOQ_LOG_HUGE, "[%s][MoQ] -- -- Group Order: %"SCNu8" (%s)\n", - imquic_get_connection_name(moq->conn), group_order, imquic_moq_group_order_str(group_order)); - parameters.group_order_set = TRUE; - parameters.group_order = group_order; - uint8_t content_exists = bytes[offset]; - offset++; - IMQUIC_MOQ_CHECK_ERR(content_exists > 1, error, IMQUIC_MOQ_PROTOCOL_VIOLATION, 0, "Invalid Content Exists value"); - IMQUIC_MOQ_CHECK_ERR(content_exists && blen-offset == 0, NULL, 0, 0, "Broken SUBSCRIBE_OK"); - IMQUIC_LOG(IMQUIC_MOQ_LOG_HUGE, "[%s][MoQ] -- Content Exists: %"SCNu8"\n", - imquic_get_connection_name(moq->conn), content_exists); - if(content_exists > 0) { - parameters.largest_object_set = TRUE; - parameters.largest_object.group = imquic_read_varint(&bytes[offset], blen-offset, &length); - IMQUIC_MOQ_CHECK_ERR(length == 0 || length >= blen-offset, NULL, 0, 0, "Broken SUBSCRIBE_OK"); - offset += length; - IMQUIC_LOG(IMQUIC_MOQ_LOG_HUGE, "[%s][MoQ] -- Largest Group ID: %"SCNu64"\n", - imquic_get_connection_name(moq->conn), parameters.largest_object.group); - parameters.largest_object.object = imquic_read_varint(&bytes[offset], blen-offset, &length); - IMQUIC_MOQ_CHECK_ERR(length == 0 || length > blen-offset, NULL, 0, 0, "Broken SUBSCRIBE_OK"); - offset += length; - IMQUIC_LOG(IMQUIC_MOQ_LOG_HUGE, "[%s][MoQ] -- Largest Object ID: %"SCNu64"\n", - imquic_get_connection_name(moq->conn), parameters.largest_object.object); - } - IMQUIC_MOQ_CHECK_ERR(blen-offset == 0, NULL, 0, 0, "Broken SUBSCRIBE_OK"); - } - uint64_t params_num = imquic_read_varint(&bytes[offset], blen-offset, &length); + uint64_t params_num = imquic_read_moqint(moq->version, &bytes[offset], blen-offset, &length); IMQUIC_MOQ_CHECK_ERR(params_num > 0 && (length == 0 || length >= blen-offset), NULL, 0, 0, "Broken SUBSCRIBE_OK"); IMQUIC_MOQ_CHECK_ERR(params_num == 0 && (length == 0 || length > blen-offset), NULL, 0, 0, "Broken SUBSCRIBE_OK"); offset += length; @@ -3212,121 +2862,43 @@ size_t imquic_moq_parse_subscribe_ok(imquic_moq_context *moq, uint8_t *bytes, si for(i = 0; iversion >= IMQUIC_MOQ_VERSION_16) { - /* The message contains extensions */ - ext_len = imquic_read_varint(&bytes[offset], blen-offset, &length); - IMQUIC_MOQ_CHECK_ERR(length == 0 || (ext_len > 0 && length >= blen-offset), NULL, 0, 0, "Broken SUBSCRIBE_OK"); - offset += length; - IMQUIC_LOG(IMQUIC_MOQ_LOG_HUGE, "[%s][MoQ] -- Track Extensions Length: %"SCNu64"\n", - imquic_get_connection_name(moq->conn), ext_len); - ext_offset = offset; - IMQUIC_MOQ_CHECK_ERR(ext_len > blen-offset, NULL, 0, 0, "Broken SUBSCRIBE_OK"); - offset += ext_len; - } - GList *track_extensions = NULL; - if(ext_offset > 0 && ext_len > 0) { + IMQUIC_MOQ_CHECK_ERR(error && *error, error, IMQUIC_MOQ_PROTOCOL_VIOLATION, 0, "Error parsing SUBSCRIBE_OK parameters"); + } + size_t prop_offset = 0, prop_len = 0; + prop_len = imquic_read_moqint(moq->version, &bytes[offset], blen-offset, &length); + IMQUIC_MOQ_CHECK_ERR(length == 0 || (prop_len > 0 && length >= blen-offset), NULL, 0, 0, "Broken SUBSCRIBE_OK"); + offset += length; + IMQUIC_LOG(IMQUIC_MOQ_LOG_HUGE, "[%s][MoQ] -- Track Properties Length: %"SCNu64"\n", + imquic_get_connection_name(moq->conn), prop_len); + prop_offset = offset; + IMQUIC_MOQ_CHECK_ERR(prop_len > blen-offset, NULL, 0, 0, "Broken SUBSCRIBE_OK"); + offset += prop_len; + GList *track_properties = NULL; + if(prop_offset > 0 && prop_len > 0) { /* TODO Check Protocol Violation cases */ - track_extensions = imquic_moq_parse_object_extensions(moq->version, &bytes[ext_offset], ext_len); + track_properties = imquic_moq_parse_properties(moq->version, &bytes[prop_offset], prop_len); } #ifdef HAVE_QLOG if(moq->conn->qlog != NULL && moq->conn->qlog->moq) { json_t *message = imquic_qlog_moq_message_prepare("subscribe_ok"); - json_object_set_new(message, "request_id", json_integer(request_id)); - if(moq->version >= IMQUIC_MOQ_VERSION_12) - json_object_set_new(message, "track_alias", json_integer(track_alias)); - if(moq->version <= IMQUIC_MOQ_VERSION_14) { - json_object_set_new(message, "expires", json_integer(parameters.expires)); - json_object_set_new(message, "group_order", json_integer(parameters.group_order)); - json_object_set_new(message, "content_exists", json_integer(parameters.largest_object_set)); - if(parameters.largest_object_set) { - json_object_set_new(message, "largest_group_id", json_integer(parameters.largest_object.group)); - json_object_set_new(message, "largest_object_id", json_integer(parameters.largest_object.object)); - } - } + if(moq->version <= IMQUIC_MOQ_VERSION_16) + json_object_set_new(message, "request_id", json_integer(request_id)); + json_object_set_new(message, "track_alias", json_integer(track_alias)); json_object_set_new(message, "number_of_parameters", json_integer(params_num)); imquic_qlog_moq_message_add_request_parameters(message, moq->version, ¶meters, "parameters"); - if(moq->version >= IMQUIC_MOQ_VERSION_16) - imquic_qlog_moq_message_add_extensions(message, track_extensions, "track_extensions"); - imquic_moq_qlog_control_message_parsed(moq->conn->qlog, moq->control_stream_id, bytes-3, offset+3, message); + imquic_qlog_moq_message_add_properties(message, track_properties, "track_properties"); + imquic_moq_qlog_control_message_parsed(moq->conn->qlog, + (moq_stream ? moq_stream->stream_id : imquic_moq_get_control_stream(moq)), bytes-3, offset+3, message); } #endif + if(moq_stream != NULL) + moq_stream->request_state = IMQUIC_MOQ_REQUEST_STATE_OK; /* Notify the application */ if(moq->conn->socket && moq->conn->socket->callbacks.moq.subscribe_accepted) { moq->conn->socket->callbacks.moq.subscribe_accepted(moq->conn, - request_id, track_alias, ¶meters, track_extensions); - } - g_list_free_full(track_extensions, (GDestroyNotify)imquic_moq_object_extension_free); - if(error) - *error = 0; - return offset; -} - -size_t imquic_moq_parse_subscribe_error(imquic_moq_context *moq, uint8_t *bytes, size_t blen, uint8_t *error) { - if(error) - *error = IMQUIC_MOQ_UNKNOWN_ERROR; - if(bytes == NULL || blen < 1) - return 0; - size_t offset = 0; - uint8_t length = 0; - uint64_t request_id = imquic_read_varint(&bytes[offset], blen-offset, &length); - IMQUIC_MOQ_CHECK_ERR(length == 0 || length >= blen-offset, NULL, 0, 0, "Broken SUBSCRIBE_ERROR"); - offset += length; - IMQUIC_LOG(IMQUIC_MOQ_LOG_HUGE, "[%s][MoQ] -- Request ID: %"SCNu64"\n", - imquic_get_connection_name(moq->conn), request_id); - uint64_t error_code = imquic_read_varint(&bytes[offset], blen-offset, &length); - IMQUIC_MOQ_CHECK_ERR(length == 0 || length >= blen-offset, NULL, 0, 0, "Broken SUBSCRIBE_ERROR"); - offset += length; - uint64_t recvd_error_code = error_code; - if(moq->version < IMQUIC_MOQ_VERSION_15) - error_code = imquic_moq_request_error_code_from_legacy(moq->version, error_code); - IMQUIC_LOG(IMQUIC_MOQ_LOG_HUGE, "[%s][MoQ] -- Error Code: %s (%"SCNu64")\n", - imquic_get_connection_name(moq->conn), imquic_moq_request_error_code_str(error_code), recvd_error_code); - uint64_t rs_len = imquic_read_varint(&bytes[offset], blen-offset, &length); - IMQUIC_MOQ_CHECK_ERR(length == 0 || length > blen-offset, NULL, 0, 0, "Broken SUBSCRIBE_ERROR"); - offset += length; - char reason[1024], *reason_str = NULL; - if(rs_len > 0) { - IMQUIC_MOQ_CHECK_ERR(rs_len > blen-offset, NULL, 0, 0, "Broken SUBSCRIBE_ERROR"); - IMQUIC_MOQ_CHECK_ERR(rs_len > sizeof(reason), error, IMQUIC_MOQ_PROTOCOL_VIOLATION, 0, "Invalid reason length"); - int reason_len = (int)rs_len; - IMQUIC_LOG(IMQUIC_MOQ_LOG_HUGE, "[%s][MoQ] -- Reason Phrase: %.*s\n", - imquic_get_connection_name(moq->conn), reason_len, &bytes[offset]); - if(reason_len > 0) { - g_snprintf(reason, sizeof(reason), "%.*s", reason_len, &bytes[offset]); - reason_str = reason; - } - offset += reason_len; - } - uint64_t track_alias = 0; - if(moq->version < IMQUIC_MOQ_VERSION_12) { - IMQUIC_MOQ_CHECK_ERR(blen-offset == 0, NULL, 0, 0, "Broken SUBSCRIBE_ERROR"); - track_alias = imquic_read_varint(&bytes[offset], blen-offset, &length); - IMQUIC_MOQ_CHECK_ERR(length == 0 || length > blen-offset, NULL, 0, 0, "Broken SUBSCRIBE_ERROR"); - offset += length; - IMQUIC_LOG(IMQUIC_MOQ_LOG_HUGE, "[%s][MoQ] -- Track Alias: %"SCNu64"\n", - imquic_get_connection_name(moq->conn), track_alias); - } -#ifdef HAVE_QLOG - if(moq->conn->qlog != NULL && moq->conn->qlog->moq) { - json_t *message = imquic_qlog_moq_message_prepare(moq->version >= IMQUIC_MOQ_VERSION_15 ? "request_error" : "subscribe_error"); - json_object_set_new(message, "request_id", json_integer(request_id)); - if(moq->version < IMQUIC_MOQ_VERSION_12) - json_object_set_new(message, "track_alias", json_integer(track_alias)); - json_object_set_new(message, "error_code", json_integer(recvd_error_code)); - if(reason_str != NULL) - json_object_set_new(message, "reason", json_string(reason_str)); - imquic_moq_qlog_control_message_parsed(moq->conn->qlog, moq->control_stream_id, bytes-3, offset+3, message); - } -#endif - /* Notify the application */ - if(moq->conn->socket && moq->conn->socket->callbacks.moq.subscribe_error) { - moq->conn->socket->callbacks.moq.subscribe_error(moq->conn, - request_id, error_code, reason_str, track_alias, 0); + request_id, track_alias, ¶meters, track_properties); } + g_list_free_full(track_properties, (GDestroyNotify)imquic_moq_property_free); if(error) *error = 0; return offset; @@ -3339,7 +2911,7 @@ size_t imquic_moq_parse_unsubscribe(imquic_moq_context *moq, uint8_t *bytes, siz return 0; size_t offset = 0; uint8_t length = 0; - uint64_t request_id = imquic_read_varint(&bytes[offset], blen-offset, &length); + uint64_t request_id = imquic_read_moqint(moq->version, &bytes[offset], blen-offset, &length); IMQUIC_MOQ_CHECK_ERR(length == 0 || length > blen-offset, NULL, 0, 0, "Broken UNSUBSCRIBE"); offset += length; IMQUIC_LOG(IMQUIC_MOQ_LOG_HUGE, "[%s][MoQ] -- Request ID: %"SCNu64"\n", @@ -3356,7 +2928,7 @@ size_t imquic_moq_parse_unsubscribe(imquic_moq_context *moq, uint8_t *bytes, siz if(moq->conn->qlog != NULL && moq->conn->qlog->moq) { json_t *message = imquic_qlog_moq_message_prepare("unsubscribe"); json_object_set_new(message, "request_id", json_integer(request_id)); - imquic_moq_qlog_control_message_parsed(moq->conn->qlog, moq->control_stream_id, bytes-3, offset+3, message); + imquic_moq_qlog_control_message_parsed(moq->conn->qlog, imquic_moq_get_control_stream(moq), bytes-3, offset+3, message); } #endif /* Notify the application */ @@ -3367,29 +2939,38 @@ size_t imquic_moq_parse_unsubscribe(imquic_moq_context *moq, uint8_t *bytes, siz return offset; } -size_t imquic_moq_parse_publish_done(imquic_moq_context *moq, uint8_t *bytes, size_t blen, uint8_t *error) { +size_t imquic_moq_parse_publish_done(imquic_moq_context *moq, imquic_moq_stream *moq_stream, uint8_t *bytes, size_t blen, uint8_t *error) { if(error) *error = IMQUIC_MOQ_UNKNOWN_ERROR; if(bytes == NULL || blen < 1) return 0; + IMQUIC_MOQ_CHECK_ERR((moq->version >= IMQUIC_MOQ_VERSION_17 && (moq_stream == NULL || moq_stream->request_type != IMQUIC_MOQ_PUBLISH || + moq_stream->request_sender || moq_stream->request_state == IMQUIC_MOQ_REQUEST_STATE_NEW || + moq_stream->request_state == IMQUIC_MOQ_REQUEST_STATE_ERROR || moq_stream->request_state == IMQUIC_MOQ_REQUEST_STATE_DONE)), + error, IMQUIC_MOQ_PROTOCOL_VIOLATION, 0, "Invalid use of PUBLISH_DONE on bidirectional request"); size_t offset = 0; uint8_t length = 0; - uint64_t request_id = imquic_read_varint(&bytes[offset], blen-offset, &length); - IMQUIC_MOQ_CHECK_ERR(length == 0 || length >= blen-offset, NULL, 0, 0, "Broken PUBLISH_DONE"); - offset += length; - IMQUIC_LOG(IMQUIC_MOQ_LOG_HUGE, "[%s][MoQ] -- Request ID: %"SCNu64"\n", - imquic_get_connection_name(moq->conn), request_id); - uint64_t status_code = imquic_read_varint(&bytes[offset], blen-offset, &length); + uint64_t request_id = 0; + if(moq->version <= IMQUIC_MOQ_VERSION_16) { + request_id = imquic_read_moqint(moq->version, &bytes[offset], blen-offset, &length); + IMQUIC_MOQ_CHECK_ERR(length == 0 || length > blen-offset, NULL, 0, 0, "Broken REQUEST_OK"); + offset += length; + IMQUIC_LOG(IMQUIC_MOQ_LOG_HUGE, "[%s][MoQ] -- Request ID: %"SCNu64"\n", + imquic_get_connection_name(moq->conn), request_id); + } else { + request_id = moq_stream->request_id; + } + uint64_t status_code = imquic_read_moqint(moq->version, &bytes[offset], blen-offset, &length); IMQUIC_MOQ_CHECK_ERR(length == 0 || length >= blen-offset, NULL, 0, 0, "Broken PUBLISH_DONE"); offset += length; IMQUIC_LOG(IMQUIC_MOQ_LOG_HUGE, "[%s][MoQ] -- Status Code: %s (%"SCNu64")\n", imquic_get_connection_name(moq->conn), imquic_moq_pub_done_code_str(status_code), status_code); - uint64_t streams_count = imquic_read_varint(&bytes[offset], blen-offset, &length); + uint64_t streams_count = imquic_read_moqint(moq->version, &bytes[offset], blen-offset, &length); IMQUIC_MOQ_CHECK_ERR(length == 0 || length >= blen-offset, NULL, 0, 0, "Broken PUBLISH_DONE"); offset += length; IMQUIC_LOG(IMQUIC_MOQ_LOG_HUGE, "[%s][MoQ] -- Streams Count: %"SCNu64"\n", imquic_get_connection_name(moq->conn), streams_count); - uint64_t rs_len = imquic_read_varint(&bytes[offset], blen-offset, &length); + uint64_t rs_len = imquic_read_moqint(moq->version, &bytes[offset], blen-offset, &length); IMQUIC_MOQ_CHECK_ERR(length == 0 || length > blen-offset, NULL, 0, 0, "Broken PUBLISH_DONE"); offset += length; char reason[1024], *reason_str = NULL; @@ -3408,14 +2989,18 @@ size_t imquic_moq_parse_publish_done(imquic_moq_context *moq, uint8_t *bytes, si #ifdef HAVE_QLOG if(moq->conn->qlog != NULL && moq->conn->qlog->moq) { json_t *message = imquic_qlog_moq_message_prepare("publish_done"); - json_object_set_new(message, "request_id", json_integer(request_id)); + if(moq->version <= IMQUIC_MOQ_VERSION_16) + json_object_set_new(message, "request_id", json_integer(request_id)); json_object_set_new(message, "status_code", json_integer(status_code)); json_object_set_new(message, "streams_count", json_integer(streams_count)); if(reason_str != NULL) json_object_set_new(message, "reason", json_string(reason_str)); - imquic_moq_qlog_control_message_parsed(moq->conn->qlog, moq->control_stream_id, bytes-3, offset+3, message); + imquic_moq_qlog_control_message_parsed(moq->conn->qlog, + (moq_stream ? moq_stream->stream_id : imquic_moq_get_control_stream(moq)), bytes-3, offset+3, message); } #endif + if(moq_stream != NULL) + moq_stream->request_state = IMQUIC_MOQ_REQUEST_STATE_DONE; /* Notify the application */ if(moq->conn->socket && moq->conn->socket->callbacks.moq.publish_done) { moq->conn->socket->callbacks.moq.publish_done(moq->conn, @@ -3431,27 +3016,32 @@ size_t imquic_moq_parse_subscribe_namespace(imquic_moq_context *moq, imquic_moq_ *error = IMQUIC_MOQ_UNKNOWN_ERROR; if(bytes == NULL || blen < 1) return 0; - IMQUIC_MOQ_CHECK_ERR((moq->version >= IMQUIC_MOQ_VERSION_16 && (moq_stream == NULL || - !moq_stream->namespace_publisher || !g_atomic_int_compare_and_exchange(&moq_stream->subscribe_namespace_state, 0, 1))), - error, IMQUIC_MOQ_PROTOCOL_VIOLATION, 0, "Invalid use of SUBSCRIBE_NAMESPACE"); + IMQUIC_MOQ_CHECK_ERR((moq_stream == NULL || + moq_stream->request_sender || moq_stream->request_state != IMQUIC_MOQ_REQUEST_STATE_NEW), + error, IMQUIC_MOQ_PROTOCOL_VIOLATION, 0, "Invalid use of SUBSCRIBE_NAMESPACE on bidirectional request"); size_t offset = 0; uint8_t length = 0; - uint64_t request_id = imquic_read_varint(&bytes[offset], blen-offset, &length); + uint64_t request_id = imquic_read_moqint(moq->version, &bytes[offset], blen-offset, &length); IMQUIC_MOQ_CHECK_ERR(length == 0 || length >= blen-offset, NULL, 0, 0, "Broken SUBSCRIBE_NAMESPACE"); offset += length; IMQUIC_LOG(IMQUIC_MOQ_LOG_HUGE, "[%s][MoQ] -- Request ID: %"SCNu64"\n", imquic_get_connection_name(moq->conn), request_id); + uint64_t required_id_delta = 0; + if(moq->version >= IMQUIC_MOQ_VERSION_17) { + required_id_delta = imquic_read_moqint(moq->version, &bytes[offset], blen-offset, &length); + IMQUIC_MOQ_CHECK_ERR(length == 0 || length >= blen-offset, NULL, 0, 0, "Broken SUBSCRIBE_NAMESPACE"); + offset += length; + IMQUIC_LOG(IMQUIC_MOQ_LOG_HUGE, "[%s][MoQ] -- Required Request ID Delta: %"SCNu64"\n", + imquic_get_connection_name(moq->conn), required_id_delta); + } imquic_moq_namespace tns[32]; memset(&tns, 0, sizeof(tns)); uint64_t tns_num = 0, i = 0; IMQUIC_MOQ_PARSE_NAMESPACES(IMQUIC_MOQ_SUBSCRIBE_NAMESPACE, tns_num, i, "Broken SUBSCRIBE_NAMESPACE", FALSE); - imquic_moq_subscribe_namespace_options subscribe_options = IMQUIC_MOQ_WANT_PUBLISH_AND_NAMESPACE; - if(moq->version >= IMQUIC_MOQ_VERSION_16) { - subscribe_options = imquic_read_varint(&bytes[offset], blen-offset, &length); - IMQUIC_MOQ_CHECK_ERR(length == 0 || length >= blen-offset || subscribe_options > IMQUIC_MOQ_WANT_PUBLISH_AND_NAMESPACE, NULL, 0, 0, "Broken SUBSCRIBE_NAMESPACE"); - offset += length; - } - uint64_t params_num = imquic_read_varint(&bytes[offset], blen-offset, &length); + imquic_moq_subscribe_namespace_options subscribe_options = imquic_read_moqint(moq->version, &bytes[offset], blen-offset, &length); + IMQUIC_MOQ_CHECK_ERR(length == 0 || length >= blen-offset || subscribe_options > IMQUIC_MOQ_WANT_PUBLISH_AND_NAMESPACE, NULL, 0, 0, "Broken SUBSCRIBE_NAMESPACE"); + offset += length; + uint64_t params_num = imquic_read_moqint(moq->version, &bytes[offset], blen-offset, &length); IMQUIC_MOQ_CHECK_ERR(params_num > 0 && (length == 0 || length >= blen-offset), NULL, 0, 0, "Broken SUBSCRIBE_NAMESPACE"); IMQUIC_MOQ_CHECK_ERR(params_num == 0 && (length == 0 || length > blen-offset), NULL, 0, 0, "Broken SUBSCRIBE_NAMESPACE"); offset += length; @@ -3462,41 +3052,45 @@ size_t imquic_moq_parse_subscribe_namespace(imquic_moq_context *moq, imquic_moq_ for(i = 0; iconn->qlog != NULL && moq->conn->qlog->moq) { - if(moq->version >= IMQUIC_MOQ_VERSION_16 && moq_stream != NULL) + if(moq_stream != NULL) imquic_moq_qlog_stream_type_set(moq->conn->qlog, FALSE, moq_stream->stream_id, "subscribe_namespace"); json_t *message = imquic_qlog_moq_message_prepare("subscribe_namespace"); json_object_set_new(message, "request_id", json_integer(request_id)); + if(moq->version >= IMQUIC_MOQ_VERSION_17) + json_object_set_new(message, "required_request_id_delta", json_integer(required_id_delta)); imquic_qlog_moq_message_add_namespace(message, &tns[0], "track_namespace_prefix"); - if(moq->version >= IMQUIC_MOQ_VERSION_16) - json_object_set_new(message, "subscribe_options", json_integer(subscribe_options)); + json_object_set_new(message, "subscribe_options", json_integer(subscribe_options)); json_object_set_new(message, "number_of_parameters", json_integer(params_num)); imquic_qlog_moq_message_add_request_parameters(message, moq->version, ¶meters, "parameters"); imquic_moq_qlog_control_message_parsed(moq->conn->qlog, - (moq_stream ? moq_stream->stream_id : moq->control_stream_id), bytes-3, offset+3, message); + (moq_stream ? moq_stream->stream_id : imquic_moq_get_control_stream(moq)), bytes-3, offset+3, message); } #endif /* Make sure this is in line with the expected request ID */ IMQUIC_MOQ_CHECK_ERR(!moq_is_request_id_valid(moq, request_id, FALSE), error, IMQUIC_MOQ_INVALID_REQUEST_ID, 0, "Invalid Request ID"); moq->expected_request_id = request_id + IMQUIC_MOQ_REQUEST_ID_INCREMENT; - /* If we're on a recent version of MoQ, track this subscription via its request ID */ - if(moq->version >= IMQUIC_MOQ_VERSION_16 && moq_stream != NULL) { + /* If we're on a recent version of MoQ, track this request via its request ID */ + if(moq_stream != NULL) { moq_stream->request_id = request_id; + moq_stream->request_state = IMQUIC_MOQ_REQUEST_STATE_SENT; moq_stream->namespace_prefix = moq_stream->last_tuple = imquic_moq_namespace_duplicate(tns); while(moq_stream->last_tuple->next != NULL) moq_stream->last_tuple = moq_stream->last_tuple->next; moq_stream->namespace_prefix_size = tns_num; imquic_mutex_lock(&moq->mutex); - g_hash_table_insert(moq->tns_subscriptions_by_id, imquic_dup_uint64(request_id), moq_stream); + g_hash_table_insert(moq->streams_by_reqid, imquic_dup_uint64(request_id), moq_stream); imquic_mutex_unlock(&moq->mutex); } /* Notify the application */ if(moq->conn->socket && moq->conn->socket->callbacks.moq.incoming_subscribe_namespace) { moq->conn->socket->callbacks.moq.incoming_subscribe_namespace(moq->conn, - request_id, &tns[0], subscribe_options, ¶meters); + request_id, required_id_delta, + (tns_num > 0 ? &tns[0] : NULL), + subscribe_options, ¶meters); } else { /* No handler for this request, let's reject it ourselves */ imquic_moq_reject_subscribe_namespace(moq->conn, request_id, IMQUIC_MOQ_REQERR_NOT_SUPPORTED, "Not handled", 0); @@ -3506,149 +3100,69 @@ size_t imquic_moq_parse_subscribe_namespace(imquic_moq_context *moq, imquic_moq_ return offset; } -size_t imquic_moq_parse_subscribe_namespace_ok(imquic_moq_context *moq, uint8_t *bytes, size_t blen, uint8_t *error) { - if(error) - *error = IMQUIC_MOQ_UNKNOWN_ERROR; - if(bytes == NULL || blen < 1) - return 0; - size_t offset = 0; - uint8_t length = 0; - uint64_t request_id = imquic_read_varint(&bytes[offset], blen-offset, &length); - IMQUIC_MOQ_CHECK_ERR(length == 0 || length > blen-offset, NULL, 0, 0, "Broken SUBSCRIBE_NAMESPACE_OK"); - offset += length; - IMQUIC_LOG(IMQUIC_MOQ_LOG_HUGE, "[%s][MoQ] -- Request ID: %"SCNu64"\n", - imquic_get_connection_name(moq->conn), request_id); -#ifdef HAVE_QLOG - if(moq->conn->qlog != NULL && moq->conn->qlog->moq) { - json_t *message = imquic_qlog_moq_message_prepare(moq->version >= IMQUIC_MOQ_VERSION_15 ? "request_ok" : "subscribe_namespace_ok"); - json_object_set_new(message, "request_id", json_integer(request_id)); - imquic_moq_qlog_control_message_parsed(moq->conn->qlog, moq->control_stream_id, bytes-3, offset+3, message); - } -#endif - /* Notify the application */ - if(moq->conn->socket && moq->conn->socket->callbacks.moq.subscribe_namespace_accepted) { - imquic_moq_request_parameters params; - imquic_moq_request_parameters_init_defaults(¶ms); - moq->conn->socket->callbacks.moq.subscribe_namespace_accepted(moq->conn, request_id, ¶ms); - } - if(error) - *error = 0; - return offset; -} - -size_t imquic_moq_parse_subscribe_namespace_error(imquic_moq_context *moq, uint8_t *bytes, size_t blen, uint8_t *error) { - if(error) - *error = IMQUIC_MOQ_UNKNOWN_ERROR; - if(bytes == NULL || blen < 1) - return 0; - size_t offset = 0; - uint8_t length = 0; - uint64_t request_id = imquic_read_varint(&bytes[offset], blen-offset, &length); - IMQUIC_MOQ_CHECK_ERR(length == 0 || length >= blen-offset, NULL, 0, 0, "Broken SUBSCRIBE_NAMESPACE_ERROR"); - offset += length; - IMQUIC_LOG(IMQUIC_MOQ_LOG_HUGE, "[%s][MoQ] -- Request ID: %"SCNu64"\n", - imquic_get_connection_name(moq->conn), request_id); - uint64_t error_code = imquic_read_varint(&bytes[offset], blen-offset, &length); - IMQUIC_MOQ_CHECK_ERR(length == 0 || length >= blen-offset, NULL, 0, 0, "Broken SUBSCRIBE_NAMESPACE_ERROR"); - offset += length; - IMQUIC_LOG(IMQUIC_MOQ_LOG_HUGE, "[%s][MoQ] -- Error Code: %s (%"SCNu64")\n", - imquic_get_connection_name(moq->conn), imquic_moq_error_code_str(error_code), error_code); - uint64_t rs_len = imquic_read_varint(&bytes[offset], blen-offset, &length); - IMQUIC_MOQ_CHECK_ERR(length == 0 || length > blen-offset, NULL, 0, 0, "Broken SUBSCRIBE_NAMESPACE_ERROR"); - offset += length; - char reason[1024], *reason_str = NULL; - if(rs_len > 0) { - IMQUIC_MOQ_CHECK_ERR(rs_len > blen-offset, NULL, 0, 0, "Broken SUBSCRIBE_NAMESPACE_ERROR"); - IMQUIC_MOQ_CHECK_ERR(rs_len > sizeof(reason), error, IMQUIC_MOQ_PROTOCOL_VIOLATION, 0, "Invalid reason length"); - int reason_len = (int)rs_len; - IMQUIC_LOG(IMQUIC_MOQ_LOG_HUGE, "[%s][MoQ] -- Reason Phrase: %.*s\n", - imquic_get_connection_name(moq->conn), reason_len, &bytes[offset]); - if(reason_len > 0) { - g_snprintf(reason, sizeof(reason), "%.*s", reason_len, &bytes[offset]); - reason_str = reason; - } - offset += reason_len; - } -#ifdef HAVE_QLOG - if(moq->conn->qlog != NULL && moq->conn->qlog->moq) { - json_t *message = imquic_qlog_moq_message_prepare(moq->version >= IMQUIC_MOQ_VERSION_15 ? "request_error" : "subscribe_namespace_error"); - json_object_set_new(message, "request_id", json_integer(request_id)); - json_object_set_new(message, "error_code", json_integer(error_code)); - if(reason_str != NULL) - json_object_set_new(message, "reason", json_string(reason_str)); - imquic_moq_qlog_control_message_parsed(moq->conn->qlog, moq->control_stream_id, bytes-3, offset+3, message); - } -#endif - /* Notify the application */ - if(moq->conn->socket && moq->conn->socket->callbacks.moq.subscribe_namespace_error) - moq->conn->socket->callbacks.moq.subscribe_namespace_error(moq->conn, request_id, error_code, reason_str, 0); - if(error) - *error = 0; - return offset; -} - -size_t imquic_moq_parse_unsubscribe_namespace(imquic_moq_context *moq, uint8_t *bytes, size_t blen, uint8_t *error) { +size_t imquic_moq_parse_namespace(imquic_moq_context *moq, imquic_moq_stream *moq_stream, uint8_t *bytes, size_t blen, uint8_t *error) { if(error) *error = IMQUIC_MOQ_UNKNOWN_ERROR; if(bytes == NULL || blen < 1) return 0; + IMQUIC_MOQ_CHECK_ERR((moq_stream == NULL || moq_stream->request_type != IMQUIC_MOQ_SUBSCRIBE_NAMESPACE || + !moq_stream->request_sender || (moq_stream->request_state != IMQUIC_MOQ_REQUEST_STATE_OK && moq_stream->request_state != IMQUIC_MOQ_REQUEST_STATE_UPDATE_SENT)), + error, IMQUIC_MOQ_PROTOCOL_VIOLATION, 0, "Invalid use of NAMESPACE on bidirectional request"); size_t offset = 0; uint8_t length = 0; imquic_moq_namespace tns[32]; memset(&tns, 0, sizeof(tns)); uint64_t tns_num = 0, i = 0; - uint64_t request_id = 0; - if(moq->version >= IMQUIC_MOQ_VERSION_15) { - request_id = imquic_read_varint(&bytes[offset], blen-offset, &length); - IMQUIC_MOQ_CHECK_ERR(length == 0 || length >= blen-offset, NULL, 0, 0, "Broken UNSUBSCRIBE_NAMESPACE"); - offset += length; - } - if(moq->version <= IMQUIC_MOQ_VERSION_14) { - IMQUIC_MOQ_PARSE_NAMESPACES(IMQUIC_MOQ_UNSUBSCRIBE_NAMESPACE, tns_num, i, "Broken UNSUBSCRIBE_NAMESPACE", TRUE); - } + IMQUIC_MOQ_PARSE_NAMESPACES(IMQUIC_MOQ_NAMESPACE, tns_num, i, "Broken NAMESPACE", TRUE); + IMQUIC_MOQ_CHECK_ERR((tns_num + moq_stream->namespace_prefix_size) > 32, error, IMQUIC_MOQ_PROTOCOL_VIOLATION, 0, "Invalid number of namespaces"); #ifdef HAVE_QLOG if(moq->conn->qlog != NULL && moq->conn->qlog->moq) { - json_t *message = imquic_qlog_moq_message_prepare("unsubscribe_namespace"); - if(moq->version >= IMQUIC_MOQ_VERSION_15) - json_object_set_new(message, "request_id", json_integer(request_id)); - if(moq->version <= IMQUIC_MOQ_VERSION_14) - imquic_qlog_moq_message_add_namespace(message, &tns[0], "track_namespace"); - imquic_moq_qlog_control_message_parsed(moq->conn->qlog, moq->control_stream_id, bytes-3, offset+3, message); + json_t *message = imquic_qlog_moq_message_prepare("namespace"); + imquic_qlog_moq_message_add_namespace(message, (tns_num > 0 ? &tns[0] : NULL), "track_namespace_suffix"); + imquic_moq_qlog_control_message_parsed(moq->conn->qlog, moq_stream->stream_id, bytes-3, offset+3, message); } #endif /* Notify the application */ - if(moq->conn->socket && moq->conn->socket->callbacks.moq.incoming_unsubscribe_namespace) - moq->conn->socket->callbacks.moq.incoming_unsubscribe_namespace(moq->conn, request_id, &tns[0]); + if(moq->conn->socket && moq->conn->socket->callbacks.moq.incoming_namespace) { + /* Prepare the full track namespace */ + if(tns_num > 0) + moq_stream->last_tuple->next = &tns[0]; + moq->conn->socket->callbacks.moq.incoming_namespace(moq->conn, moq_stream->request_id, moq_stream->namespace_prefix); + moq_stream->last_tuple->next = NULL; + } if(error) *error = 0; return offset; } -size_t imquic_moq_parse_namespace(imquic_moq_context *moq, imquic_moq_stream *moq_stream, uint8_t *bytes, size_t blen, uint8_t *error) { +size_t imquic_moq_parse_namespace_done(imquic_moq_context *moq, imquic_moq_stream *moq_stream, uint8_t *bytes, size_t blen, uint8_t *error) { if(error) *error = IMQUIC_MOQ_UNKNOWN_ERROR; if(bytes == NULL || blen < 1) return 0; + IMQUIC_MOQ_CHECK_ERR((moq_stream == NULL || moq_stream->request_type != IMQUIC_MOQ_SUBSCRIBE_NAMESPACE || + !moq_stream->request_sender || (moq_stream->request_state != IMQUIC_MOQ_REQUEST_STATE_OK && moq_stream->request_state != IMQUIC_MOQ_REQUEST_STATE_UPDATE_SENT)), + error, IMQUIC_MOQ_PROTOCOL_VIOLATION, 0, "Invalid use of NAMESPACE_DONE on bidirectional request"); size_t offset = 0; uint8_t length = 0; imquic_moq_namespace tns[32]; memset(&tns, 0, sizeof(tns)); uint64_t tns_num = 0, i = 0; - IMQUIC_MOQ_PARSE_NAMESPACES(IMQUIC_MOQ_NAMESPACE, tns_num, i, "Broken NAMESPACE", TRUE); + IMQUIC_MOQ_PARSE_NAMESPACES(IMQUIC_MOQ_NAMESPACE_DONE, tns_num, i, "Broken NAMESPACE_DONE", TRUE); IMQUIC_MOQ_CHECK_ERR((tns_num + moq_stream->namespace_prefix_size) > 32, error, IMQUIC_MOQ_PROTOCOL_VIOLATION, 0, "Invalid number of namespaces"); #ifdef HAVE_QLOG if(moq->conn->qlog != NULL && moq->conn->qlog->moq) { - json_t *message = imquic_qlog_moq_message_prepare("namespace"); + json_t *message = imquic_qlog_moq_message_prepare("namespace_done"); imquic_qlog_moq_message_add_namespace(message, (tns_num > 0 ? &tns[0] : NULL), "track_namespace_suffix"); imquic_moq_qlog_control_message_parsed(moq->conn->qlog, moq_stream->stream_id, bytes-3, offset+3, message); } #endif /* Notify the application */ - if(moq->conn->socket && moq->conn->socket->callbacks.moq.incoming_namespace) { + if(moq->conn->socket && moq->conn->socket->callbacks.moq.incoming_namespace_done) { /* Prepare the full track namespace */ if(tns_num > 0) moq_stream->last_tuple->next = &tns[0]; - moq->conn->socket->callbacks.moq.incoming_namespace(moq->conn, moq_stream->request_id, moq_stream->namespace_prefix); + moq->conn->socket->callbacks.moq.incoming_namespace_done(moq->conn, moq_stream->request_id, moq_stream->namespace_prefix); moq_stream->last_tuple->next = NULL; } if(error) @@ -3656,31 +3170,37 @@ size_t imquic_moq_parse_namespace(imquic_moq_context *moq, imquic_moq_stream *mo return offset; } -size_t imquic_moq_parse_namespace_done(imquic_moq_context *moq, imquic_moq_stream *moq_stream, uint8_t *bytes, size_t blen, uint8_t *error) { +size_t imquic_moq_parse_publish_blocked(imquic_moq_context *moq, imquic_moq_stream *moq_stream, uint8_t *bytes, size_t blen, uint8_t *error) { if(error) *error = IMQUIC_MOQ_UNKNOWN_ERROR; if(bytes == NULL || blen < 1) return 0; + IMQUIC_MOQ_CHECK_ERR((moq_stream == NULL || moq_stream->request_type != IMQUIC_MOQ_SUBSCRIBE_NAMESPACE || + !moq_stream->request_sender || (moq_stream->request_state != IMQUIC_MOQ_REQUEST_STATE_OK && moq_stream->request_state != IMQUIC_MOQ_REQUEST_STATE_UPDATE_SENT)), + error, IMQUIC_MOQ_PROTOCOL_VIOLATION, 0, "Invalid use of PUBLISH_BLOCKED on bidirectional request"); size_t offset = 0; uint8_t length = 0; imquic_moq_namespace tns[32]; memset(&tns, 0, sizeof(tns)); uint64_t tns_num = 0, i = 0; - IMQUIC_MOQ_PARSE_NAMESPACES(IMQUIC_MOQ_NAMESPACE_DONE, tns_num, i, "Broken NAMESPACE_DONE", TRUE); + IMQUIC_MOQ_PARSE_NAMESPACES(IMQUIC_MOQ_NAMESPACE_DONE, tns_num, i, "Broken PUBLISH_BLOCKED", TRUE); IMQUIC_MOQ_CHECK_ERR((tns_num + moq_stream->namespace_prefix_size) > 32, error, IMQUIC_MOQ_PROTOCOL_VIOLATION, 0, "Invalid number of namespaces"); + imquic_moq_name tn = { 0 }; + IMQUIC_MOQ_PARSE_TRACKNAME("Broken PUBLISH_BLOCKED", FALSE); #ifdef HAVE_QLOG if(moq->conn->qlog != NULL && moq->conn->qlog->moq) { - json_t *message = imquic_qlog_moq_message_prepare("namespace_done"); + json_t *message = imquic_qlog_moq_message_prepare("publish_blocked"); imquic_qlog_moq_message_add_namespace(message, (tns_num > 0 ? &tns[0] : NULL), "track_namespace_suffix"); + imquic_qlog_moq_message_add_track(message, &tn); imquic_moq_qlog_control_message_parsed(moq->conn->qlog, moq_stream->stream_id, bytes-3, offset+3, message); } #endif /* Notify the application */ - if(moq->conn->socket && moq->conn->socket->callbacks.moq.incoming_namespace_done) { + if(moq->conn->socket && moq->conn->socket->callbacks.moq.incoming_publish_blocked) { /* Prepare the full track namespace */ if(tns_num > 0) moq_stream->last_tuple->next = &tns[0]; - moq->conn->socket->callbacks.moq.incoming_namespace_done(moq->conn, moq_stream->request_id, moq_stream->namespace_prefix); + moq->conn->socket->callbacks.moq.incoming_publish_blocked(moq->conn, moq_stream->request_id, moq_stream->namespace_prefix, &tn); moq_stream->last_tuple->next = NULL; } if(error) @@ -3688,77 +3208,68 @@ size_t imquic_moq_parse_namespace_done(imquic_moq_context *moq, imquic_moq_strea return offset; } -size_t imquic_moq_parse_fetch(imquic_moq_context *moq, uint8_t *bytes, size_t blen, uint8_t *error) { +size_t imquic_moq_parse_fetch(imquic_moq_context *moq, imquic_moq_stream *moq_stream, uint8_t *bytes, size_t blen, uint8_t *error) { if(error) *error = IMQUIC_MOQ_UNKNOWN_ERROR; if(bytes == NULL || blen < 1) return 0; + IMQUIC_MOQ_CHECK_ERR((moq->version >= IMQUIC_MOQ_VERSION_17 && (moq_stream == NULL || + moq_stream->request_sender || moq_stream->request_state != IMQUIC_MOQ_REQUEST_STATE_NEW)), + error, IMQUIC_MOQ_PROTOCOL_VIOLATION, 0, "Invalid use of FETCH on bidirectional request"); size_t offset = 0; uint8_t length = 0; - uint64_t request_id = imquic_read_varint(&bytes[offset], blen-offset, &length); + uint64_t request_id = imquic_read_moqint(moq->version, &bytes[offset], blen-offset, &length); IMQUIC_MOQ_CHECK_ERR(length == 0 || length >= blen-offset, NULL, 0, 0, "Broken FETCH"); offset += length; IMQUIC_LOG(IMQUIC_MOQ_LOG_HUGE, "[%s][MoQ] -- Request ID: %"SCNu64"\n", imquic_get_connection_name(moq->conn), request_id); + uint64_t required_id_delta = 0; + if(moq->version >= IMQUIC_MOQ_VERSION_17) { + required_id_delta = imquic_read_moqint(moq->version, &bytes[offset], blen-offset, &length); + IMQUIC_MOQ_CHECK_ERR(length == 0 || length >= blen-offset, NULL, 0, 0, "Broken FETCH"); + offset += length; + IMQUIC_LOG(IMQUIC_MOQ_LOG_HUGE, "[%s][MoQ] -- Required Request ID Delta: %"SCNu64"\n", + imquic_get_connection_name(moq->conn), required_id_delta); + } /* Move on */ imquic_moq_namespace tns[32]; memset(&tns, 0, sizeof(tns)); imquic_moq_name tn = { 0 }; - /* For versions older than v15, we need to parse some attributes manually, - * but we'll add them to the parameters object for the application anyway */ - imquic_moq_request_parameters parameters; - imquic_moq_request_parameters_init_defaults(¶meters); - if(moq->version <= IMQUIC_MOQ_VERSION_14) { - parameters.subscriber_priority_set = TRUE; - parameters.subscriber_priority = bytes[offset]; - offset++; - IMQUIC_MOQ_CHECK_ERR(blen-offset == 0, NULL, 0, 0, "Broken FETCH"); - IMQUIC_LOG(IMQUIC_MOQ_LOG_HUGE, "[%s][MoQ] -- -- Subscriber Priority: %"SCNu8")\n", - imquic_get_connection_name(moq->conn), parameters.subscriber_priority); - uint8_t group_order = bytes[offset]; - offset++; - IMQUIC_MOQ_CHECK_ERR(group_order > IMQUIC_MOQ_ORDERING_DESCENDING, error, IMQUIC_MOQ_PROTOCOL_VIOLATION, 0, "Invalid Group Order value"); - IMQUIC_MOQ_CHECK_ERR(blen-offset == 0, NULL, 0, 0, "Broken FETCH"); - IMQUIC_LOG(IMQUIC_MOQ_LOG_HUGE, "[%s][MoQ] -- -- Group Order: %"SCNu8" (%s)\n", - imquic_get_connection_name(moq->conn), group_order, imquic_moq_group_order_str(group_order)); - parameters.group_order_set = TRUE; - parameters.group_order = group_order; - } imquic_moq_fetch_type type = IMQUIC_MOQ_FETCH_STANDALONE; imquic_moq_location_range range = { 0 }; uint64_t joining_request_id = 0, joining_start = 0; - type = imquic_read_varint(&bytes[offset], blen-offset, &length); + type = imquic_read_moqint(moq->version, &bytes[offset], blen-offset, &length); IMQUIC_MOQ_CHECK_ERR(length == 0 || length >= blen-offset, NULL, 0, 0, "Broken FETCH"); offset += length; if(type == IMQUIC_MOQ_FETCH_STANDALONE) { uint64_t tns_num = 0, i = 0; IMQUIC_MOQ_PARSE_NAMESPACES(IMQUIC_MOQ_FETCH, tns_num, i, "Broken FETCH", FALSE); IMQUIC_MOQ_PARSE_TRACKNAME("Broken FETCH", FALSE); - range.start.group = imquic_read_varint(&bytes[offset], blen-offset, &length); + range.start.group = imquic_read_moqint(moq->version, &bytes[offset], blen-offset, &length); IMQUIC_MOQ_CHECK_ERR(length == 0 || length >= blen-offset, NULL, 0, 0, "Broken FETCH"); offset += length; IMQUIC_LOG(IMQUIC_MOQ_LOG_HUGE, "[%s][MoQ] -- -- Start Group: %"SCNu64"\n", imquic_get_connection_name(moq->conn), range.start.group); - range.start.object = imquic_read_varint(&bytes[offset], blen-offset, &length); + range.start.object = imquic_read_moqint(moq->version, &bytes[offset], blen-offset, &length); IMQUIC_MOQ_CHECK_ERR(length == 0 || length >= blen-offset, NULL, 0, 0, "Broken FETCH"); offset += length; IMQUIC_LOG(IMQUIC_MOQ_LOG_HUGE, "[%s][MoQ] -- -- Start Object: %"SCNu64"\n", imquic_get_connection_name(moq->conn), range.start.object); - range.end.group = imquic_read_varint(&bytes[offset], blen-offset, &length); + range.end.group = imquic_read_moqint(moq->version, &bytes[offset], blen-offset, &length); IMQUIC_MOQ_CHECK_ERR(length == 0 || length >= blen-offset, NULL, 0, 0, "Broken FETCH"); offset += length; IMQUIC_LOG(IMQUIC_MOQ_LOG_HUGE, "[%s][MoQ] -- -- End Group: %"SCNu64"\n", imquic_get_connection_name(moq->conn), range.end.group); - range.end.object = imquic_read_varint(&bytes[offset], blen-offset, &length); + range.end.object = imquic_read_moqint(moq->version, &bytes[offset], blen-offset, &length); IMQUIC_MOQ_CHECK_ERR(length == 0 || length >= blen-offset, NULL, 0, 0, "Broken FETCH"); offset += length; IMQUIC_LOG(IMQUIC_MOQ_LOG_HUGE, "[%s][MoQ] -- -- End Object: %"SCNu64"\n", imquic_get_connection_name(moq->conn), range.end.object); } else if(type == IMQUIC_MOQ_FETCH_JOINING_RELATIVE || type == IMQUIC_MOQ_FETCH_JOINING_ABSOLUTE) { - joining_request_id = imquic_read_varint(&bytes[offset], blen-offset, &length); + joining_request_id = imquic_read_moqint(moq->version, &bytes[offset], blen-offset, &length); IMQUIC_MOQ_CHECK_ERR(length == 0 || length >= blen-offset, NULL, 0, 0, "Broken FETCH"); offset += length; - joining_start = imquic_read_varint(&bytes[offset], blen-offset, &length); + joining_start = imquic_read_moqint(moq->version, &bytes[offset], blen-offset, &length); IMQUIC_MOQ_CHECK_ERR(length == 0 || length >= blen-offset, NULL, 0, 0, "Broken FETCH"); offset += length; } else { @@ -3766,7 +3277,9 @@ size_t imquic_moq_parse_fetch(imquic_moq_context *moq, uint8_t *bytes, size_t bl imquic_get_connection_name(moq->conn), type); return 0; } - uint64_t params_num = imquic_read_varint(&bytes[offset], blen-offset, &length); + imquic_moq_request_parameters parameters; + imquic_moq_request_parameters_init_defaults(¶meters); + uint64_t params_num = imquic_read_moqint(moq->version, &bytes[offset], blen-offset, &length); IMQUIC_MOQ_CHECK_ERR(params_num > 0 && (length == 0 || length >= blen-offset), NULL, 0, 0, "Broken FETCH"); IMQUIC_MOQ_CHECK_ERR(params_num == 0 && (length == 0 || length > blen-offset), NULL, 0, 0, "Broken FETCH"); offset += length; @@ -3776,16 +3289,16 @@ size_t imquic_moq_parse_fetch(imquic_moq_context *moq, uint8_t *bytes, size_t bl for(i = 0; iconn->qlog != NULL && moq->conn->qlog->moq) { + if(moq_stream != NULL) + imquic_moq_qlog_stream_type_set(moq->conn->qlog, FALSE, moq_stream->stream_id, "fetch"); json_t *message = imquic_qlog_moq_message_prepare("fetch"); json_object_set_new(message, "request_id", json_integer(request_id)); - if(moq->version <= IMQUIC_MOQ_VERSION_14) { - json_object_set_new(message, "subscriber_priority", json_integer(parameters.subscriber_priority)); - json_object_set_new(message, "group_order", json_integer(parameters.group_order)); - } + if(moq->version >= IMQUIC_MOQ_VERSION_17) + json_object_set_new(message, "required_request_id_delta", json_integer(required_id_delta)); json_object_set_new(message, "fetch_type", json_integer(type)); if(type == IMQUIC_MOQ_FETCH_STANDALONE) { imquic_qlog_moq_message_add_namespace(message, &tns[0], "track_namespace"); @@ -3800,12 +3313,21 @@ size_t imquic_moq_parse_fetch(imquic_moq_context *moq, uint8_t *bytes, size_t bl } json_object_set_new(message, "number_of_parameters", json_integer(params_num)); imquic_qlog_moq_message_add_request_parameters(message, moq->version, ¶meters, "parameters"); - imquic_moq_qlog_control_message_parsed(moq->conn->qlog, moq->control_stream_id, bytes-3, offset+3, message); + imquic_moq_qlog_control_message_parsed(moq->conn->qlog, + (moq_stream ? moq_stream->stream_id : imquic_moq_get_control_stream(moq)), bytes-3, offset+3, message); } #endif /* Make sure this is in line with the expected request ID */ IMQUIC_MOQ_CHECK_ERR(!moq_is_request_id_valid(moq, request_id, FALSE), error, IMQUIC_MOQ_INVALID_REQUEST_ID, 0, "Invalid Request ID"); moq->expected_request_id = request_id + IMQUIC_MOQ_REQUEST_ID_INCREMENT; + /* If we're on a recent version of MoQ, track this request via its request ID */ + if(moq_stream != NULL) { + moq_stream->request_id = request_id; + moq_stream->request_state = IMQUIC_MOQ_REQUEST_STATE_SENT; + imquic_mutex_lock(&moq->mutex); + g_hash_table_insert(moq->streams_by_reqid, imquic_dup_uint64(request_id), moq_stream); + imquic_mutex_unlock(&moq->mutex); + } /* Track this fetch subscription */ imquic_moq_subscription *moq_sub = imquic_moq_subscription_create(request_id, 0); moq_sub->fetch = TRUE; @@ -3816,7 +3338,7 @@ size_t imquic_moq_parse_fetch(imquic_moq_context *moq, uint8_t *bytes, size_t bl if(type == IMQUIC_MOQ_FETCH_STANDALONE) { if(moq->conn->socket && moq->conn->socket->callbacks.moq.incoming_standalone_fetch) { moq->conn->socket->callbacks.moq.incoming_standalone_fetch(moq->conn, - request_id, &tns[0], &tn, &range, ¶meters); + request_id, required_id_delta, &tns[0], &tn, &range, ¶meters); } else { /* No handler for this request, let's reject it ourselves */ imquic_moq_reject_fetch(moq->conn, request_id, IMQUIC_MOQ_REQERR_NOT_SUPPORTED, "Not handled", 0); @@ -3824,7 +3346,7 @@ size_t imquic_moq_parse_fetch(imquic_moq_context *moq, uint8_t *bytes, size_t bl } else { if(moq->conn->socket && moq->conn->socket->callbacks.moq.incoming_joining_fetch) { moq->conn->socket->callbacks.moq.incoming_joining_fetch(moq->conn, - request_id, joining_request_id, + request_id, required_id_delta, joining_request_id, (type == IMQUIC_MOQ_FETCH_JOINING_ABSOLUTE), joining_start, ¶meters); } else { /* No handler for this request, let's reject it ourselves */ @@ -3843,7 +3365,7 @@ size_t imquic_moq_parse_fetch_cancel(imquic_moq_context *moq, uint8_t *bytes, si return 0; size_t offset = 0; uint8_t length = 0; - uint64_t request_id = imquic_read_varint(&bytes[offset], blen-offset, &length); + uint64_t request_id = imquic_read_moqint(moq->version, &bytes[offset], blen-offset, &length); IMQUIC_MOQ_CHECK_ERR(length == 0 || length > blen-offset, NULL, 0, 0, "Broken FETCH_CANCEL"); offset += length; IMQUIC_LOG(IMQUIC_MOQ_LOG_HUGE, "[%s][MoQ] -- Request ID: %"SCNu64"\n", @@ -3863,7 +3385,7 @@ size_t imquic_moq_parse_fetch_cancel(imquic_moq_context *moq, uint8_t *bytes, si if(moq->conn->qlog != NULL && moq->conn->qlog->moq) { json_t *message = imquic_qlog_moq_message_prepare("fetch_cancel"); json_object_set_new(message, "request_id", json_integer(request_id)); - imquic_moq_qlog_control_message_parsed(moq->conn->qlog, moq->control_stream_id, bytes-3, offset+3, message); + imquic_moq_qlog_control_message_parsed(moq->conn->qlog, imquic_moq_get_control_stream(moq), bytes-3, offset+3, message); } #endif /* Notify the application */ @@ -3874,50 +3396,46 @@ size_t imquic_moq_parse_fetch_cancel(imquic_moq_context *moq, uint8_t *bytes, si return offset; } -size_t imquic_moq_parse_fetch_ok(imquic_moq_context *moq, uint8_t *bytes, size_t blen, uint8_t *error) { +size_t imquic_moq_parse_fetch_ok(imquic_moq_context *moq, imquic_moq_stream *moq_stream, uint8_t *bytes, size_t blen, uint8_t *error) { if(error) *error = IMQUIC_MOQ_UNKNOWN_ERROR; if(bytes == NULL || blen < 1) return 0; + IMQUIC_MOQ_CHECK_ERR((moq->version >= IMQUIC_MOQ_VERSION_17 && (moq_stream == NULL || moq_stream->request_type != IMQUIC_MOQ_FETCH || + !moq_stream->request_sender || moq_stream->request_state != IMQUIC_MOQ_REQUEST_STATE_SENT)), + error, IMQUIC_MOQ_PROTOCOL_VIOLATION, 0, "Invalid use of FETCH_OK on bidirectional request"); size_t offset = 0; uint8_t length = 0; - uint64_t request_id = imquic_read_varint(&bytes[offset], blen-offset, &length); - IMQUIC_MOQ_CHECK_ERR(length == 0 || length >= blen-offset, NULL, 0, 0, "Broken FETCH_OK"); - offset += length; - IMQUIC_LOG(IMQUIC_MOQ_LOG_HUGE, "[%s][MoQ] -- Request ID: %"SCNu64"\n", - imquic_get_connection_name(moq->conn), request_id); - IMQUIC_MOQ_CHECK_ERR(blen-offset == 0, NULL, 0, 0, "Broken FETCH_OK"); - /* For versions older than v15, we need to parse some attributes manually, - * but we'll add them to the parameters object for the application anyway */ - imquic_moq_request_parameters parameters; - imquic_moq_request_parameters_init_defaults(¶meters); - if(moq->version <= IMQUIC_MOQ_VERSION_14) { - uint8_t group_order = bytes[offset]; - offset++; - IMQUIC_MOQ_CHECK_ERR(group_order > IMQUIC_MOQ_ORDERING_DESCENDING, error, IMQUIC_MOQ_PROTOCOL_VIOLATION, 0, "Invalid Group Order value"); - IMQUIC_MOQ_CHECK_ERR(blen-offset == 0, NULL, 0, 0, "Broken FETCH_OK"); - IMQUIC_LOG(IMQUIC_MOQ_LOG_HUGE, "[%s][MoQ] -- -- Group Order: %"SCNu8" (%s)\n", - imquic_get_connection_name(moq->conn), group_order, imquic_moq_group_order_str(group_order)); - parameters.group_order_set = TRUE; - parameters.group_order = group_order; + uint64_t request_id = 0; + if(moq->version <= IMQUIC_MOQ_VERSION_16) { + request_id = imquic_read_moqint(moq->version, &bytes[offset], blen-offset, &length); + IMQUIC_MOQ_CHECK_ERR(length == 0 || length > blen-offset, NULL, 0, 0, "Broken REQUEST_OK"); + offset += length; + IMQUIC_LOG(IMQUIC_MOQ_LOG_HUGE, "[%s][MoQ] -- Request ID: %"SCNu64"\n", + imquic_get_connection_name(moq->conn), request_id); + } else { + request_id = moq_stream->request_id; } + IMQUIC_MOQ_CHECK_ERR(blen-offset == 0, NULL, 0, 0, "Broken FETCH_OK"); uint8_t end_of_track = bytes[offset]; offset++; IMQUIC_MOQ_CHECK_ERR(blen-offset == 0, NULL, 0, 0, "Broken FETCH_OK"); IMQUIC_LOG(IMQUIC_MOQ_LOG_HUGE, "[%s][MoQ] -- -- End Of Track: %"SCNu8")\n", imquic_get_connection_name(moq->conn), end_of_track); imquic_moq_location largest = { 0 }; - largest.group = imquic_read_varint(&bytes[offset], blen-offset, &length); + largest.group = imquic_read_moqint(moq->version, &bytes[offset], blen-offset, &length); IMQUIC_MOQ_CHECK_ERR(length == 0 || length >= blen-offset, NULL, 0, 0, "Broken FETCH_OK"); offset += length; IMQUIC_LOG(IMQUIC_MOQ_LOG_HUGE, "[%s][MoQ] -- Largest Group ID: %"SCNu64"\n", imquic_get_connection_name(moq->conn), largest.group); - largest.object = imquic_read_varint(&bytes[offset], blen-offset, &length); + largest.object = imquic_read_moqint(moq->version, &bytes[offset], blen-offset, &length); IMQUIC_MOQ_CHECK_ERR(length == 0 || length >= blen-offset, NULL, 0, 0, "Broken FETCH_OK"); offset += length; IMQUIC_LOG(IMQUIC_MOQ_LOG_HUGE, "[%s][MoQ] -- Largest Object ID: %"SCNu64"\n", imquic_get_connection_name(moq->conn), largest.object); - uint64_t params_num = imquic_read_varint(&bytes[offset], blen-offset, &length); + imquic_moq_request_parameters parameters; + imquic_moq_request_parameters_init_defaults(¶meters); + uint64_t params_num = imquic_read_moqint(moq->version, &bytes[offset], blen-offset, &length); IMQUIC_MOQ_CHECK_ERR(params_num > 0 && (length == 0 || length >= blen-offset), NULL, 0, 0, "Broken FETCH_OK"); IMQUIC_MOQ_CHECK_ERR(params_num == 0 && (length == 0 || length > blen-offset), NULL, 0, 0, "Broken FETCH_OK"); offset += length; @@ -3927,120 +3445,59 @@ size_t imquic_moq_parse_fetch_ok(imquic_moq_context *moq, uint8_t *bytes, size_t for(i = 0; iversion >= IMQUIC_MOQ_VERSION_16) { - /* The message contains extensions */ - ext_len = imquic_read_varint(&bytes[offset], blen-offset, &length); - IMQUIC_MOQ_CHECK_ERR(length == 0 || (ext_len > 0 && length >= blen-offset), NULL, 0, 0, "Broken FETCH_OK"); - offset += length; - IMQUIC_LOG(IMQUIC_MOQ_LOG_HUGE, "[%s][MoQ] -- Track Extensions Length: %"SCNu64"\n", - imquic_get_connection_name(moq->conn), ext_len); - ext_offset = offset; - IMQUIC_MOQ_CHECK_ERR(ext_len > blen-offset, NULL, 0, 0, "Broken FETCH_OK"); - offset += ext_len; - } - GList *track_extensions = NULL; - if(ext_offset > 0 && ext_len > 0) { + IMQUIC_MOQ_CHECK_ERR(error && *error, error, IMQUIC_MOQ_PROTOCOL_VIOLATION, 0, "Error parsing FETCH_OK parameters"); + } + size_t prop_offset = 0, prop_len = 0; + prop_len = imquic_read_moqint(moq->version, &bytes[offset], blen-offset, &length); + IMQUIC_MOQ_CHECK_ERR(length == 0 || (prop_len > 0 && length >= blen-offset), NULL, 0, 0, "Broken FETCH_OK"); + offset += length; + IMQUIC_LOG(IMQUIC_MOQ_LOG_HUGE, "[%s][MoQ] -- Track Properties Length: %"SCNu64"\n", + imquic_get_connection_name(moq->conn), prop_len); + prop_offset = offset; + IMQUIC_MOQ_CHECK_ERR(prop_len > blen-offset, NULL, 0, 0, "Broken FETCH_OK"); + offset += prop_len; + GList *track_properties = NULL; + if(prop_offset > 0 && prop_len > 0) { /* TODO Check Protocol Violation cases */ - track_extensions = imquic_moq_parse_object_extensions(moq->version, &bytes[ext_offset], ext_len); + track_properties = imquic_moq_parse_properties(moq->version, &bytes[prop_offset], prop_len); } #ifdef HAVE_QLOG if(moq->conn->qlog != NULL && moq->conn->qlog->moq) { json_t *message = imquic_qlog_moq_message_prepare("fetch_ok"); - json_object_set_new(message, "request_id", json_integer(request_id)); - if(moq->version <= IMQUIC_MOQ_VERSION_14) - json_object_set_new(message, "group_order", json_integer(parameters.group_order)); + if(moq->version <= IMQUIC_MOQ_VERSION_16) + json_object_set_new(message, "request_id", json_integer(request_id)); json_object_set_new(message, "end_of_track", json_integer(end_of_track)); json_object_set_new(message, "largest_group_id", json_integer(largest.group)); json_object_set_new(message, "largest_object_id", json_integer(largest.object)); json_object_set_new(message, "number_of_parameters", json_integer(params_num)); imquic_qlog_moq_message_add_request_parameters(message, moq->version, ¶meters, "parameters"); - if(moq->version >= IMQUIC_MOQ_VERSION_16) - imquic_qlog_moq_message_add_extensions(message, track_extensions, "track_extensions"); - imquic_moq_qlog_control_message_parsed(moq->conn->qlog, moq->control_stream_id, bytes-3, offset+3, message); + imquic_qlog_moq_message_add_properties(message, track_properties, "track_properties"); + imquic_moq_qlog_control_message_parsed(moq->conn->qlog, + (moq_stream ? moq_stream->stream_id : imquic_moq_get_control_stream(moq)), bytes-3, offset+3, message); } #endif + if(moq_stream != NULL) + moq_stream->request_state = IMQUIC_MOQ_REQUEST_STATE_OK; /* Notify the application */ if(moq->conn->socket && moq->conn->socket->callbacks.moq.fetch_accepted) - moq->conn->socket->callbacks.moq.fetch_accepted(moq->conn, request_id, &largest, ¶meters, track_extensions); - g_list_free_full(track_extensions, (GDestroyNotify)imquic_moq_object_extension_free); + moq->conn->socket->callbacks.moq.fetch_accepted(moq->conn, request_id, &largest, ¶meters, track_properties); + g_list_free_full(track_properties, (GDestroyNotify)imquic_moq_property_free); if(error) *error = 0; return offset; } -size_t imquic_moq_parse_fetch_error(imquic_moq_context *moq, uint8_t *bytes, size_t blen, uint8_t *error) { +size_t imquic_moq_parse_track_status(imquic_moq_context *moq, imquic_moq_stream *moq_stream, uint8_t *bytes, size_t blen, uint8_t *error) { if(error) *error = IMQUIC_MOQ_UNKNOWN_ERROR; if(bytes == NULL || blen < 1) return 0; + IMQUIC_MOQ_CHECK_ERR((moq->version >= IMQUIC_MOQ_VERSION_17 && (moq_stream == NULL || + moq_stream->request_sender || moq_stream->request_state != IMQUIC_MOQ_REQUEST_STATE_NEW)), + error, IMQUIC_MOQ_PROTOCOL_VIOLATION, 0, "Invalid use of TRACK_STATUS on bidirectional request"); size_t offset = 0; uint8_t length = 0; - uint64_t request_id = imquic_read_varint(&bytes[offset], blen-offset, &length); - IMQUIC_MOQ_CHECK_ERR(length == 0 || length >= blen-offset, NULL, 0, 0, "Broken FETCH_ERROR"); - offset += length; - IMQUIC_LOG(IMQUIC_MOQ_LOG_HUGE, "[%s][MoQ] -- Request ID: %"SCNu64"\n", - imquic_get_connection_name(moq->conn), request_id); - uint64_t error_code = imquic_read_varint(&bytes[offset], blen-offset, &length); - IMQUIC_MOQ_CHECK_ERR(length == 0 || length >= blen-offset, NULL, 0, 0, "Broken FETCH_ERROR"); - offset += length; - uint64_t recvd_error_code = error_code; - if(moq->version < IMQUIC_MOQ_VERSION_15) - error_code = imquic_moq_request_error_code_from_legacy(moq->version, error_code); - IMQUIC_LOG(IMQUIC_MOQ_LOG_HUGE, "[%s][MoQ] -- Error Code: %s (%"SCNu64")\n", - imquic_get_connection_name(moq->conn), imquic_moq_request_error_code_str(error_code), recvd_error_code); - uint64_t rs_len = imquic_read_varint(&bytes[offset], blen-offset, &length); - IMQUIC_MOQ_CHECK_ERR(length == 0 || length > blen-offset, NULL, 0, 0, "Broken FETCH_ERROR"); - offset += length; - char reason[1024], *reason_str = NULL; - if(rs_len > 0) { - IMQUIC_MOQ_CHECK_ERR(rs_len > blen-offset, NULL, 0, 0, "Broken FETCH_ERROR"); - IMQUIC_MOQ_CHECK_ERR(rs_len > sizeof(reason), error, IMQUIC_MOQ_PROTOCOL_VIOLATION, 0, "Invalid reason length"); - int reason_len = (int)rs_len; - IMQUIC_LOG(IMQUIC_MOQ_LOG_HUGE, "[%s][MoQ] -- Reason Phrase: %.*s\n", - imquic_get_connection_name(moq->conn), reason_len, &bytes[offset]); - if(reason_len > 0) { - g_snprintf(reason, sizeof(reason), "%.*s", reason_len, &bytes[offset]); - reason_str = reason; - } - offset += reason_len; - } -#ifdef HAVE_QLOG - if(moq->conn->qlog != NULL && moq->conn->qlog->moq) { - json_t *message = imquic_qlog_moq_message_prepare(moq->version >= IMQUIC_MOQ_VERSION_15 ? "request_error" : "fetch_error"); - json_object_set_new(message, "request_id", json_integer(request_id)); - json_object_set_new(message, "error_code", json_integer(recvd_error_code)); - if(reason_str != NULL) - json_object_set_new(message, "reason", json_string(reason_str)); - imquic_moq_qlog_control_message_parsed(moq->conn->qlog, moq->control_stream_id, bytes-3, offset+3, message); - } -#endif - /* Notify the application */ - if(moq->conn->socket && moq->conn->socket->callbacks.moq.fetch_error) - moq->conn->socket->callbacks.moq.fetch_error(moq->conn, request_id, error_code, reason_str, 0); - if(error) - *error = 0; - return offset; -} - -size_t imquic_moq_parse_track_status(imquic_moq_context *moq, uint8_t *bytes, size_t blen, uint8_t *error) { - if(error) - *error = IMQUIC_MOQ_UNKNOWN_ERROR; - if(bytes == NULL || blen < 1) - return 0; - if(moq->version < IMQUIC_MOQ_VERSION_13) { - /* Since the format changed too much, we ignored it on versions older than v13 */ - IMQUIC_LOG(IMQUIC_LOG_WARN, "[%s][MoQ] Ignoring %s on a connection using %s\n", - imquic_get_connection_name(moq->conn), imquic_moq_message_type_str(IMQUIC_MOQ_TRACK_STATUS, moq->version), - imquic_moq_version_str(moq->version)); - return blen; - } - size_t offset = 0; - uint8_t length = 0; - uint64_t request_id = imquic_read_varint(&bytes[offset], blen-offset, &length); + uint64_t request_id = imquic_read_moqint(moq->version, &bytes[offset], blen-offset, &length); IMQUIC_MOQ_CHECK_ERR(length == 0 || length >= blen-offset, NULL, 0, 0, "Broken TRACK_STATUS"); offset += length; IMQUIC_LOG(IMQUIC_MOQ_LOG_HUGE, "[%s][MoQ] -- Request ID: %"SCNu64"\n", @@ -4052,62 +3509,9 @@ size_t imquic_moq_parse_track_status(imquic_moq_context *moq, uint8_t *bytes, si IMQUIC_MOQ_PARSE_NAMESPACES(IMQUIC_MOQ_TRACK_STATUS, tns_num, i, "Broken TRACK_STATUS", FALSE); imquic_moq_name tn = { 0 }; IMQUIC_MOQ_PARSE_TRACKNAME("Broken TRACK_STATUS", FALSE); - /* For versions older than v15, we need to parse some attributes manually, - * but we'll add them to the parameters object for the application anyway */ imquic_moq_request_parameters parameters; imquic_moq_request_parameters_init_defaults(¶meters); - if(moq->version <= IMQUIC_MOQ_VERSION_14) { - parameters.subscriber_priority_set = TRUE; - parameters.subscriber_priority = bytes[offset]; - offset++; - IMQUIC_MOQ_CHECK_ERR(blen-offset == 0, NULL, 0, 0, "Broken TRACK_STATUS"); - IMQUIC_LOG(IMQUIC_MOQ_LOG_HUGE, "[%s][MoQ] -- -- Subscriber Priority: %"SCNu8")\n", - imquic_get_connection_name(moq->conn), parameters.subscriber_priority); - uint8_t group_order = bytes[offset]; - offset++; - IMQUIC_MOQ_CHECK_ERR(group_order > IMQUIC_MOQ_ORDERING_DESCENDING, error, IMQUIC_MOQ_PROTOCOL_VIOLATION, 0, "Invalid Group Order value"); - IMQUIC_MOQ_CHECK_ERR(blen-offset == 0, NULL, 0, 0, "Broken TRACK_STATUS"); - IMQUIC_LOG(IMQUIC_MOQ_LOG_HUGE, "[%s][MoQ] -- -- Group Order: %"SCNu8" (%s)\n", - imquic_get_connection_name(moq->conn), group_order, imquic_moq_group_order_str(group_order)); - parameters.group_order_set = TRUE; - parameters.group_order = group_order; - uint8_t forward = bytes[offset]; - IMQUIC_MOQ_CHECK_ERR(forward > 1, error, IMQUIC_MOQ_PROTOCOL_VIOLATION, 0, "Invalid Forward value"); - parameters.forward_set = TRUE; - parameters.forward = (forward > 0); - offset++; - IMQUIC_MOQ_CHECK_ERR(blen-offset == 0, NULL, 0, 0, "Broken TRACK_STATUS"); - IMQUIC_LOG(IMQUIC_MOQ_LOG_HUGE, "[%s][MoQ] -- -- Forward: %"SCNu8")\n", - imquic_get_connection_name(moq->conn), parameters.forward); - uint64_t filter = imquic_read_varint(&bytes[offset], blen-offset, &length); - IMQUIC_MOQ_CHECK_ERR(length == 0 || length >= blen-offset, NULL, 0, 0, "Broken TRACK_STATUS"); - IMQUIC_MOQ_CHECK_ERR((filter < 0x1 || filter > 0x4), error, IMQUIC_MOQ_PROTOCOL_VIOLATION, 0, "Invalid Filter type"); - offset += length; - IMQUIC_LOG(IMQUIC_MOQ_LOG_HUGE, "[%s][MoQ] -- Filter type: %s (%"SCNu64")\n", - imquic_get_connection_name(moq->conn), imquic_moq_filter_type_str(filter), filter); - parameters.subscription_filter_set = TRUE; - parameters.subscription_filter.type = filter; - if(filter == IMQUIC_MOQ_FILTER_ABSOLUTE_START || filter == IMQUIC_MOQ_FILTER_ABSOLUTE_RANGE) { - parameters.subscription_filter.start_location.group = imquic_read_varint(&bytes[offset], blen-offset, &length); - IMQUIC_MOQ_CHECK_ERR(length == 0 || length >= blen-offset, NULL, 0, 0, "Broken TRACK_STATUS"); - offset += length; - IMQUIC_LOG(IMQUIC_MOQ_LOG_HUGE, "[%s][MoQ] -- -- Start Group: %"SCNu64")\n", - imquic_get_connection_name(moq->conn), parameters.subscription_filter.start_location.group); - parameters.subscription_filter.start_location.object = imquic_read_varint(&bytes[offset], blen-offset, &length); - IMQUIC_MOQ_CHECK_ERR(length == 0 || length >= blen-offset, NULL, 0, 0, "Broken TRACK_STATUS"); - offset += length; - IMQUIC_LOG(IMQUIC_MOQ_LOG_HUGE, "[%s][MoQ] -- -- Start Object: %"SCNu64")\n", - imquic_get_connection_name(moq->conn), parameters.subscription_filter.start_location.object); - } - if(filter == IMQUIC_MOQ_FILTER_ABSOLUTE_RANGE) { - parameters.subscription_filter.end_group = imquic_read_varint(&bytes[offset], blen-offset, &length); - IMQUIC_MOQ_CHECK_ERR(length == 0 || length >= blen-offset, NULL, 0, 0, "Broken TRACK_STATUS"); - offset += length; - IMQUIC_LOG(IMQUIC_MOQ_LOG_HUGE, "[%s][MoQ] -- -- End Group: %"SCNu64")\n", - imquic_get_connection_name(moq->conn), parameters.subscription_filter.end_group); - } - } - uint64_t params_num = imquic_read_varint(&bytes[offset], blen-offset, &length); + uint64_t params_num = imquic_read_moqint(moq->version, &bytes[offset], blen-offset, &length); IMQUIC_MOQ_CHECK_ERR(params_num > 0 && (length == 0 || length >= blen-offset), NULL, 0, 0, "Broken TRACK_STATUS"); IMQUIC_MOQ_CHECK_ERR(params_num == 0 && (length == 0 || length > blen-offset), NULL, 0, 0, "Broken TRACK_STATUS"); offset += length; @@ -4117,208 +3521,41 @@ size_t imquic_moq_parse_track_status(imquic_moq_context *moq, uint8_t *bytes, si for(i = 0; iconn->qlog != NULL && moq->conn->qlog->moq) { + if(moq_stream != NULL) + imquic_moq_qlog_stream_type_set(moq->conn->qlog, FALSE, moq_stream->stream_id, "track_status"); json_t *message = imquic_qlog_moq_message_prepare("track_status"); json_object_set_new(message, "request_id", json_integer(request_id)); imquic_qlog_moq_message_add_namespace(message, &tns[0], "track_namespace"); imquic_qlog_moq_message_add_track(message, &tn); - if(moq->version <= IMQUIC_MOQ_VERSION_14) { - json_object_set_new(message, "subscriber_priority", json_integer(parameters.subscriber_priority)); - json_object_set_new(message, "group_order", json_integer(parameters.group_order)); - json_object_set_new(message, "forward", json_integer(parameters.forward)); - json_object_set_new(message, "filter_type", json_integer(parameters.subscription_filter.type)); - if(parameters.subscription_filter.type == IMQUIC_MOQ_FILTER_ABSOLUTE_START || parameters.subscription_filter.type == IMQUIC_MOQ_FILTER_ABSOLUTE_RANGE) { - json_object_set_new(message, "start_group", json_integer(parameters.subscription_filter.start_location.group)); - json_object_set_new(message, "start_object", json_integer(parameters.subscription_filter.start_location.object)); - } - if(parameters.subscription_filter.type == IMQUIC_MOQ_FILTER_ABSOLUTE_RANGE) - json_object_set_new(message, "end_group", json_integer(parameters.subscription_filter.end_group)); - } json_object_set_new(message, "number_of_parameters", json_integer(params_num)); imquic_qlog_moq_message_add_request_parameters(message, moq->version, ¶meters, "parameters"); - imquic_moq_qlog_control_message_parsed(moq->conn->qlog, moq->control_stream_id, bytes-3, offset+3, message); + imquic_moq_qlog_control_message_parsed(moq->conn->qlog, + (moq_stream ? moq_stream->stream_id : imquic_moq_get_control_stream(moq)), bytes-3, offset+3, message); } #endif /* Make sure this is in line with the expected request ID */ IMQUIC_MOQ_CHECK_ERR(!moq_is_request_id_valid(moq, request_id, FALSE), error, IMQUIC_MOQ_INVALID_REQUEST_ID, 0, "Invalid Request ID"); moq->expected_request_id = request_id + IMQUIC_MOQ_REQUEST_ID_INCREMENT; - /* Notify the application */ - if(moq->conn->socket && moq->conn->socket->callbacks.moq.incoming_track_status) { - moq->conn->socket->callbacks.moq.incoming_track_status(moq->conn, - request_id, &tns[0], &tn, ¶meters); - } else { - /* No handler for this request, let's reject it ourselves */ - imquic_moq_reject_track_status(moq->conn, request_id, IMQUIC_MOQ_REQERR_NOT_SUPPORTED, "Not handled", 0); - } - if(error) - *error = 0; - return offset; -} - -size_t imquic_moq_parse_track_status_ok(imquic_moq_context *moq, uint8_t *bytes, size_t blen, uint8_t *error) { - if(error) - *error = IMQUIC_MOQ_UNKNOWN_ERROR; - if(bytes == NULL || blen < 1) - return 0; - if(moq->version < IMQUIC_MOQ_VERSION_13) { - /* Since the format changed too much, we ignored it on versions older than v13 */ - IMQUIC_LOG(IMQUIC_LOG_WARN, "[%s][MoQ] Ignoring %s on a connection using %s\n", - imquic_get_connection_name(moq->conn), imquic_moq_message_type_str(IMQUIC_MOQ_TRACK_STATUS, moq->version), - imquic_moq_version_str(moq->version)); - return blen; - } - size_t offset = 0; - uint8_t length = 0; - uint64_t request_id = imquic_read_varint(&bytes[offset], blen-offset, &length); - IMQUIC_MOQ_CHECK_ERR(length == 0 || length >= blen-offset, NULL, 0, 0, "Broken TRACK_STATUS_OK"); - offset += length; - IMQUIC_LOG(IMQUIC_MOQ_LOG_HUGE, "[%s][MoQ] -- Request ID: %"SCNu64"\n", - imquic_get_connection_name(moq->conn), request_id); - uint64_t track_alias = 0; - if(moq->version <= IMQUIC_MOQ_VERSION_14) { - track_alias = imquic_read_varint(&bytes[offset], blen-offset, &length); - IMQUIC_MOQ_CHECK_ERR(length == 0 || length >= blen-offset, NULL, 0, 0, "Broken TRACK_STATUS_OK"); - offset += length; - IMQUIC_LOG(IMQUIC_MOQ_LOG_HUGE, "[%s][MoQ] -- Track Alias: %"SCNu64"\n", - imquic_get_connection_name(moq->conn), track_alias); - } - /* For versions older than v15, we need to parse some attributes manually, - * but we'll add them to the parameters object for the application anyway */ - imquic_moq_request_parameters parameters; - imquic_moq_request_parameters_init_defaults(¶meters); - if(moq->version <= IMQUIC_MOQ_VERSION_14) { - parameters.expires_set = TRUE; - parameters.expires = imquic_read_varint(&bytes[offset], blen-offset, &length); - IMQUIC_MOQ_CHECK_ERR(length == 0 || length >= blen-offset, NULL, 0, 0, "Broken TRACK_STATUS_OK"); - offset += length; - IMQUIC_LOG(IMQUIC_MOQ_LOG_HUGE, "[%s][MoQ] -- Expires: %"SCNu64"\n", - imquic_get_connection_name(moq->conn), parameters.expires); - IMQUIC_MOQ_CHECK_ERR(blen-offset == 0, NULL, 0, 0, "Broken TRACK_STATUS_OK"); - uint8_t group_order = bytes[offset]; - offset++; - IMQUIC_MOQ_CHECK_ERR(group_order > IMQUIC_MOQ_ORDERING_DESCENDING, error, IMQUIC_MOQ_PROTOCOL_VIOLATION, 0, "Invalid Group Order value"); - IMQUIC_MOQ_CHECK_ERR(blen-offset == 0, NULL, 0, 0, "Broken TRACK_STATUS_OK"); - IMQUIC_LOG(IMQUIC_MOQ_LOG_HUGE, "[%s][MoQ] -- -- Group Order: %"SCNu8" (%s)\n", - imquic_get_connection_name(moq->conn), group_order, imquic_moq_group_order_str(group_order)); - parameters.group_order_set = TRUE; - parameters.group_order = group_order; - uint8_t content_exists = bytes[offset]; - offset++; - IMQUIC_MOQ_CHECK_ERR(content_exists > 1, error, IMQUIC_MOQ_PROTOCOL_VIOLATION, 0, "Invalid Content Exists value"); - IMQUIC_MOQ_CHECK_ERR(content_exists && blen-offset == 0, NULL, 0, 0, "Broken TRACK_STATUS_OK"); - IMQUIC_LOG(IMQUIC_MOQ_LOG_HUGE, "[%s][MoQ] -- Content Exists: %"SCNu8"\n", - imquic_get_connection_name(moq->conn), content_exists); - if(content_exists > 0) { - parameters.largest_object_set = TRUE; - parameters.largest_object.group = imquic_read_varint(&bytes[offset], blen-offset, &length); - IMQUIC_MOQ_CHECK_ERR(length == 0 || length >= blen-offset, NULL, 0, 0, "Broken TRACK_STATUS_OK"); - offset += length; - IMQUIC_LOG(IMQUIC_MOQ_LOG_HUGE, "[%s][MoQ] -- Largest Group ID: %"SCNu64"\n", - imquic_get_connection_name(moq->conn), parameters.largest_object.group); - parameters.largest_object.object = imquic_read_varint(&bytes[offset], blen-offset, &length); - IMQUIC_MOQ_CHECK_ERR(length == 0 || length > blen-offset, NULL, 0, 0, "Broken TRACK_STATUS_OK"); - offset += length; - IMQUIC_LOG(IMQUIC_MOQ_LOG_HUGE, "[%s][MoQ] -- Largest Object ID: %"SCNu64"\n", - imquic_get_connection_name(moq->conn), parameters.largest_object.object); - } - } - uint64_t params_num = 0; - IMQUIC_MOQ_CHECK_ERR(blen-offset == 0, NULL, 0, 0, "Broken TRACK_STATUS_OK"); - params_num = imquic_read_varint(&bytes[offset], blen-offset, &length); - IMQUIC_MOQ_CHECK_ERR(params_num > 0 && (length == 0 || length >= blen-offset), NULL, 0, 0, "Broken TRACK_STATUS_OK"); - IMQUIC_MOQ_CHECK_ERR(params_num == 0 && (length == 0 || length > blen-offset), NULL, 0, 0, "Broken TRACK_STATUS_OK"); - offset += length; - IMQUIC_LOG(IMQUIC_MOQ_LOG_HUGE, "[%s][MoQ] -- %"SCNu64" parameters:\n", - imquic_get_connection_name(moq->conn), params_num); - uint64_t i = 0, param = 0; - for(i = 0; iconn->qlog != NULL && moq->conn->qlog->moq) { - json_t *message = imquic_qlog_moq_message_prepare(moq->version >= IMQUIC_MOQ_VERSION_15 ? "request_ok" : "track_status_ok"); - json_object_set_new(message, "request_id", json_integer(request_id)); - if(moq->version <= IMQUIC_MOQ_VERSION_14) { - json_object_set_new(message, "track_alias", json_integer(track_alias)); - json_object_set_new(message, "expires", json_integer(parameters.expires)); - json_object_set_new(message, "group_order", json_integer(parameters.group_order)); - json_object_set_new(message, "content_exists", json_integer(parameters.largest_object_set)); - if(parameters.largest_object_set) { - json_object_set_new(message, "largest_group_id", json_integer(parameters.largest_object.group)); - json_object_set_new(message, "largest_object_id", json_integer(parameters.largest_object.object)); - } - } - json_object_set_new(message, "number_of_parameters", json_integer(params_num)); - imquic_qlog_moq_message_add_request_parameters(message, moq->version, ¶meters, "parameters"); - imquic_moq_qlog_control_message_parsed(moq->conn->qlog, moq->control_stream_id, bytes-3, offset+3, message); - } -#endif - /* Notify the application */ - if(moq->conn->socket && moq->conn->socket->callbacks.moq.track_status_accepted) { - moq->conn->socket->callbacks.moq.track_status_accepted(moq->conn, - request_id, track_alias, ¶meters); - } - if(error) - *error = 0; - return offset; -} - -size_t imquic_moq_parse_track_status_error(imquic_moq_context *moq, uint8_t *bytes, size_t blen, uint8_t *error) { - if(error) - *error = IMQUIC_MOQ_UNKNOWN_ERROR; - if(bytes == NULL || blen < 1) - return 0; - if(moq->version < IMQUIC_MOQ_VERSION_13) - return 0; - size_t offset = 0; - uint8_t length = 0; - uint64_t request_id = imquic_read_varint(&bytes[offset], blen-offset, &length); - IMQUIC_MOQ_CHECK_ERR(length == 0 || length >= blen-offset, NULL, 0, 0, "Broken TRACK_STATUS_ERROR"); - offset += length; - IMQUIC_LOG(IMQUIC_MOQ_LOG_HUGE, "[%s][MoQ] -- Request ID: %"SCNu64"\n", - imquic_get_connection_name(moq->conn), request_id); - uint64_t error_code = imquic_read_varint(&bytes[offset], blen-offset, &length); - IMQUIC_MOQ_CHECK_ERR(length == 0 || length >= blen-offset, NULL, 0, 0, "Broken TRACK_STATUS_ERROR"); - offset += length; - uint64_t recvd_error_code = error_code; - if(moq->version < IMQUIC_MOQ_VERSION_15) - error_code = imquic_moq_request_error_code_from_legacy(moq->version, error_code); - IMQUIC_LOG(IMQUIC_MOQ_LOG_HUGE, "[%s][MoQ] -- Error Code: %s (%"SCNu64")\n", - imquic_get_connection_name(moq->conn), imquic_moq_request_error_code_str(error_code), recvd_error_code); - uint64_t rs_len = imquic_read_varint(&bytes[offset], blen-offset, &length); - IMQUIC_MOQ_CHECK_ERR(length == 0 || length > blen-offset, NULL, 0, 0, "Broken TRACK_STATUS_ERROR"); - offset += length; - char reason[1024], *reason_str = NULL; - if(rs_len > 0) { - IMQUIC_MOQ_CHECK_ERR(rs_len > blen-offset, NULL, 0, 0, "Broken TRACK_STATUS_ERROR"); - IMQUIC_MOQ_CHECK_ERR(rs_len > sizeof(reason), error, IMQUIC_MOQ_PROTOCOL_VIOLATION, 0, "Invalid reason length"); - int reason_len = (int)rs_len; - IMQUIC_LOG(IMQUIC_MOQ_LOG_HUGE, "[%s][MoQ] -- Reason Phrase: %.*s\n", - imquic_get_connection_name(moq->conn), reason_len, &bytes[offset]); - if(reason_len > 0) { - g_snprintf(reason, sizeof(reason), "%.*s", reason_len, &bytes[offset]); - reason_str = reason; - } - offset += reason_len; - } -#ifdef HAVE_QLOG - if(moq->conn->qlog != NULL && moq->conn->qlog->moq) { - json_t *message = imquic_qlog_moq_message_prepare(moq->version >= IMQUIC_MOQ_VERSION_15 ? "request_error" : "track_status_error"); - json_object_set_new(message, "request_id", json_integer(request_id)); - json_object_set_new(message, "error_code", json_integer(recvd_error_code)); - if(reason_str != NULL) - json_object_set_new(message, "reason", json_string(reason_str)); - imquic_moq_qlog_control_message_parsed(moq->conn->qlog, moq->control_stream_id, bytes-3, offset+3, message); + /* If we're on a recent version of MoQ, track this request via its ID */ + if(moq_stream != NULL) { + moq_stream->request_id = request_id; + moq_stream->request_state = IMQUIC_MOQ_REQUEST_STATE_SENT; + imquic_mutex_lock(&moq->mutex); + g_hash_table_insert(moq->streams_by_reqid, imquic_dup_uint64(request_id), moq_stream); + imquic_mutex_unlock(&moq->mutex); } -#endif /* Notify the application */ - if(moq->conn->socket && moq->conn->socket->callbacks.moq.track_status_error) - moq->conn->socket->callbacks.moq.track_status_error(moq->conn, request_id, error_code, reason_str, 0); + if(moq->conn->socket && moq->conn->socket->callbacks.moq.incoming_track_status) { + moq->conn->socket->callbacks.moq.incoming_track_status(moq->conn, + request_id, &tns[0], &tn, ¶meters); + } else { + /* No handler for this request, let's reject it ourselves */ + imquic_moq_reject_track_status(moq->conn, request_id, IMQUIC_MOQ_REQERR_NOT_SUPPORTED, "Not handled", 0); + } if(error) *error = 0; return offset; @@ -4330,23 +3567,23 @@ size_t imquic_moq_parse_object_datagram(imquic_moq_context *moq, uint8_t *bytes, if(bytes == NULL || blen < 5) return 0; /* TODO Check EOG too */ - gboolean has_ext = FALSE, has_oid = TRUE, has_priority = TRUE; - imquic_moq_datagram_message_type_parse(moq->version, dtype, NULL, &has_ext, NULL, &has_oid, &has_priority, NULL); + gboolean has_prop = FALSE, has_oid = TRUE, has_priority = TRUE; + imquic_moq_datagram_message_type_parse(moq->version, dtype, NULL, &has_prop, NULL, &has_oid, &has_priority, NULL); size_t offset = 0; uint8_t length = 0; - uint64_t track_alias = imquic_read_varint(&bytes[offset], blen-offset, &length); + uint64_t track_alias = imquic_read_moqint(moq->version, &bytes[offset], blen-offset, &length); IMQUIC_MOQ_CHECK_ERR(length == 0 || length >= blen-offset, NULL, 0, 0, "Broken OBJECT_DATAGRAM"); offset += length; IMQUIC_LOG(IMQUIC_MOQ_LOG_HUGE, "[%s][MoQ] -- Track Alias: %"SCNu64"\n", imquic_get_connection_name(moq->conn), track_alias); - uint64_t group_id = imquic_read_varint(&bytes[offset], blen-offset, &length); + uint64_t group_id = imquic_read_moqint(moq->version, &bytes[offset], blen-offset, &length); IMQUIC_MOQ_CHECK_ERR(length == 0 || length >= blen-offset, NULL, 0, 0, "Broken OBJECT_DATAGRAM"); offset += length; IMQUIC_LOG(IMQUIC_MOQ_LOG_HUGE, "[%s][MoQ] -- Group ID: %"SCNu64"\n", imquic_get_connection_name(moq->conn), group_id); uint64_t object_id = 0; if(has_oid) { - object_id = imquic_read_varint(&bytes[offset], blen-offset, &length); + object_id = imquic_read_moqint(moq->version, &bytes[offset], blen-offset, &length); IMQUIC_MOQ_CHECK_ERR(length == 0 || length >= blen-offset, NULL, 0, 0, "Broken OBJECT_DATAGRAM"); offset += length; } @@ -4359,23 +3596,23 @@ size_t imquic_moq_parse_object_datagram(imquic_moq_context *moq, uint8_t *bytes, } IMQUIC_LOG(IMQUIC_MOQ_LOG_HUGE, "[%s][MoQ] -- Publisher Priority: %"SCNu8"\n", imquic_get_connection_name(moq->conn), priority); - size_t ext_offset = 0, ext_len = 0; - if(has_ext) { - /* The object contains extensions */ - ext_len = imquic_read_varint(&bytes[offset], blen-offset, &length); + size_t prop_offset = 0, prop_len = 0; + if(has_prop) { + /* The object contains properties */ + prop_len = imquic_read_moqint(moq->version, &bytes[offset], blen-offset, &length); IMQUIC_MOQ_CHECK_ERR(length == 0 || length >= blen-offset, NULL, 0, 0, "Broken OBJECT_DATAGRAM"); - IMQUIC_MOQ_CHECK_ERR(ext_len == 0, error, IMQUIC_MOQ_PROTOCOL_VIOLATION, 0, "Extensions length is 0 but type is OBJECT_DATAGRAM"); + IMQUIC_MOQ_CHECK_ERR(prop_len == 0, error, IMQUIC_MOQ_PROTOCOL_VIOLATION, 0, "Properties length is 0 but type is OBJECT_DATAGRAM"); offset += length; - IMQUIC_LOG(IMQUIC_MOQ_LOG_HUGE, "[%s][MoQ] -- Extensions Length: %"SCNu64"\n", - imquic_get_connection_name(moq->conn), ext_len); - ext_offset = offset; - IMQUIC_MOQ_CHECK_ERR(length == 0 || ext_len >= blen-offset, NULL, 0, 0, "Broken OBJECT_DATAGRAM"); - offset += ext_len; - } - GList *extensions = NULL; - if(ext_offset > 0 && ext_len > 0) { + IMQUIC_LOG(IMQUIC_MOQ_LOG_HUGE, "[%s][MoQ] -- Properties Length: %"SCNu64"\n", + imquic_get_connection_name(moq->conn), prop_len); + prop_offset = offset; + IMQUIC_MOQ_CHECK_ERR(length == 0 || prop_len >= blen-offset, NULL, 0, 0, "Broken OBJECT_DATAGRAM"); + offset += prop_len; + } + GList *properties = NULL; + if(prop_offset > 0 && prop_len > 0) { /* TODO Check Protocol Violation cases */ - extensions = imquic_moq_parse_object_extensions(moq->version, &bytes[ext_offset], ext_len); + properties = imquic_moq_parse_properties(moq->version, &bytes[prop_offset], prop_len); } IMQUIC_LOG(IMQUIC_MOQ_LOG_HUGE, "[%s][MoQ] -- Payload Length: %"SCNu64"\n", imquic_get_connection_name(moq->conn), blen-offset); @@ -4390,7 +3627,7 @@ size_t imquic_moq_parse_object_datagram(imquic_moq_context *moq, uint8_t *bytes, .priority = priority, .payload = (blen-offset > 0 ? &bytes[offset] : NULL), .payload_len = blen-offset, - .extensions = extensions, + .properties = properties, .delivery = IMQUIC_MOQ_USE_DATAGRAM, .end_of_stream = FALSE /* No stream is involved here */ }; @@ -4401,7 +3638,7 @@ size_t imquic_moq_parse_object_datagram(imquic_moq_context *moq, uint8_t *bytes, #endif if(moq->conn->socket && moq->conn->socket->callbacks.moq.incoming_object) moq->conn->socket->callbacks.moq.incoming_object(moq->conn, &object); - g_list_free_full(extensions, (GDestroyNotify)imquic_moq_object_extension_free); + g_list_free_full(properties, (GDestroyNotify)imquic_moq_property_free); if(error) *error = 0; return offset; @@ -4412,23 +3649,23 @@ size_t imquic_moq_parse_object_datagram_status(imquic_moq_context *moq, uint8_t *error = IMQUIC_MOQ_UNKNOWN_ERROR; if(bytes == NULL || blen < 5) return 0; - gboolean has_ext = FALSE, has_oid = TRUE, has_priority = TRUE; - imquic_moq_datagram_message_type_parse(moq->version, dtype, NULL, &has_ext, NULL, &has_oid, &has_priority, NULL); + gboolean has_prop = FALSE, has_oid = TRUE, has_priority = TRUE; + imquic_moq_datagram_message_type_parse(moq->version, dtype, NULL, &has_prop, NULL, &has_oid, &has_priority, NULL); size_t offset = 0; uint8_t length = 0; - uint64_t track_alias = imquic_read_varint(&bytes[offset], blen-offset, &length); + uint64_t track_alias = imquic_read_moqint(moq->version, &bytes[offset], blen-offset, &length); IMQUIC_MOQ_CHECK_ERR(length == 0 || length >= blen-offset, NULL, 0, 0, "Broken OBJECT_DATAGRAM_STATUS"); offset += length; IMQUIC_LOG(IMQUIC_MOQ_LOG_HUGE, "[%s][MoQ] -- Track Alias: %"SCNu64"\n", imquic_get_connection_name(moq->conn), track_alias); - uint64_t group_id = imquic_read_varint(&bytes[offset], blen-offset, &length); + uint64_t group_id = imquic_read_moqint(moq->version, &bytes[offset], blen-offset, &length); IMQUIC_MOQ_CHECK_ERR(length == 0 || length >= blen-offset, NULL, 0, 0, "Broken OBJECT_DATAGRAM_STATUS"); offset += length; IMQUIC_LOG(IMQUIC_MOQ_LOG_HUGE, "[%s][MoQ] -- Group ID: %"SCNu64"\n", imquic_get_connection_name(moq->conn), group_id); - uint64_t object_id = imquic_read_varint(&bytes[offset], blen-offset, &length); + uint64_t object_id = imquic_read_moqint(moq->version, &bytes[offset], blen-offset, &length); if(has_oid) { - object_id = imquic_read_varint(&bytes[offset], blen-offset, &length); + object_id = imquic_read_moqint(moq->version, &bytes[offset], blen-offset, &length); IMQUIC_MOQ_CHECK_ERR(length == 0 || length >= blen-offset, NULL, 0, 0, "Broken OBJECT_DATAGRAM_STATUS"); offset += length; } @@ -4441,28 +3678,27 @@ size_t imquic_moq_parse_object_datagram_status(imquic_moq_context *moq, uint8_t } IMQUIC_LOG(IMQUIC_MOQ_LOG_HUGE, "[%s][MoQ] -- Publisher Priority: %"SCNu8"\n", imquic_get_connection_name(moq->conn), priority); - size_t ext_offset = 0, ext_len = 0; - if(has_ext) { - /* The object contains extensions */ - ext_len = imquic_read_varint(&bytes[offset], blen-offset, &length); + size_t prop_offset = 0, prop_len = 0; + if(has_prop) { + /* The object contains properties */ + prop_len = imquic_read_moqint(moq->version, &bytes[offset], blen-offset, &length); IMQUIC_MOQ_CHECK_ERR(length == 0 || length >= blen-offset, NULL, 0, 0, "Broken OBJECT_DATAGRAM_STATUS"); - IMQUIC_MOQ_CHECK_ERR(ext_len == 0, error, IMQUIC_MOQ_PROTOCOL_VIOLATION, 0, "Extensions length is 0 but type is OBJECT_DATAGRAM_STATUS"); + IMQUIC_MOQ_CHECK_ERR(prop_len == 0, error, IMQUIC_MOQ_PROTOCOL_VIOLATION, 0, "Properties length is 0 but type is OBJECT_DATAGRAM_STATUS"); offset += length; - IMQUIC_LOG(IMQUIC_MOQ_LOG_HUGE, "[%s][MoQ] -- Extensions Length: %"SCNu64"\n", - imquic_get_connection_name(moq->conn), ext_len); - ext_offset = offset; - IMQUIC_MOQ_CHECK_ERR(length == 0 || ext_len >= blen-offset, NULL, 0, 0, "Broken OBJECT_DATAGRAM_STATUS"); - offset += ext_len; - } - GList *extensions = NULL; - if(ext_offset > 0 && ext_len > 0) { + IMQUIC_LOG(IMQUIC_MOQ_LOG_HUGE, "[%s][MoQ] -- Properties Length: %"SCNu64"\n", + imquic_get_connection_name(moq->conn), prop_len); + prop_offset = offset; + IMQUIC_MOQ_CHECK_ERR(length == 0 || prop_len >= blen-offset, NULL, 0, 0, "Broken OBJECT_DATAGRAM_STATUS"); + offset += prop_len; + } + GList *properties = NULL; + if(prop_offset > 0 && prop_len > 0) { /* TODO Check Protocol Violation cases */ - extensions = imquic_moq_parse_object_extensions(moq->version, &bytes[ext_offset], ext_len); + properties = imquic_moq_parse_properties(moq->version, &bytes[prop_offset], prop_len); } - uint64_t object_status = imquic_read_varint(&bytes[offset], blen-offset, &length); + uint64_t object_status = imquic_read_moqint(moq->version, &bytes[offset], blen-offset, &length); IMQUIC_MOQ_CHECK_ERR(length == 0 || length > blen-offset, NULL, 0, 0, "Broken OBJECT_DATAGRAM_STATUS"); IMQUIC_MOQ_CHECK_ERR(object_status > IMQUIC_MOQ_END_OF_TRACK, error, IMQUIC_MOQ_PROTOCOL_VIOLATION, 0, "Invalid object status"); - IMQUIC_MOQ_CHECK_ERR(object_status == IMQUIC_MOQ_OBJECT_DOESNT_EXIST && ext_len > 0, error, IMQUIC_MOQ_PROTOCOL_VIOLATION, 0, "Extensions received in object with status 'Does Not Exist'"); offset += length; IMQUIC_LOG(IMQUIC_MOQ_LOG_HUGE, "[%s][MoQ] -- Object Status: %"SCNu64"\n", imquic_get_connection_name(moq->conn), object_status); @@ -4477,7 +3713,7 @@ size_t imquic_moq_parse_object_datagram_status(imquic_moq_context *moq, uint8_t .priority = priority, .payload = NULL, .payload_len = 0, - .extensions = extensions, + .properties = properties, .delivery = IMQUIC_MOQ_USE_DATAGRAM, .end_of_stream = FALSE /* No stream is involved here */ }; @@ -4488,7 +3724,7 @@ size_t imquic_moq_parse_object_datagram_status(imquic_moq_context *moq, uint8_t #endif if(moq->conn->socket && moq->conn->socket->callbacks.moq.incoming_object) moq->conn->socket->callbacks.moq.incoming_object(moq->conn, &object); - g_list_free_full(extensions, (GDestroyNotify)imquic_moq_object_extension_free); + g_list_free_full(properties, (GDestroyNotify)imquic_moq_property_free); if(error) *error = 0; return offset; @@ -4501,26 +3737,26 @@ size_t imquic_moq_parse_subgroup_header(imquic_moq_context *moq, imquic_moq_stre return 0; size_t offset = 0; uint8_t length = 0; - uint64_t track_alias = imquic_read_varint(&bytes[offset], blen-offset, &length); + uint64_t track_alias = imquic_read_moqint(moq->version, &bytes[offset], blen-offset, &length); IMQUIC_MOQ_CHECK_ERR(length == 0 || length >= blen-offset, NULL, 0, 0, "Broken SUBGROUP_HEADER"); offset += length; IMQUIC_LOG(IMQUIC_MOQ_LOG_HUGE, "[%s][MoQ] -- Track alias: %"SCNu64"\n", imquic_get_connection_name(moq->conn), track_alias); - uint64_t group_id = imquic_read_varint(&bytes[offset], blen-offset, &length); + uint64_t group_id = imquic_read_moqint(moq->version, &bytes[offset], blen-offset, &length); IMQUIC_MOQ_CHECK_ERR(length == 0 || length >= blen-offset, NULL, 0, 0, "Broken SUBGROUP_HEADER"); offset += length; IMQUIC_LOG(IMQUIC_MOQ_LOG_HUGE, "[%s][MoQ] -- Group ID: %"SCNu64"\n", imquic_get_connection_name(moq->conn), group_id); uint64_t subgroup_id = 0; /* Starting from v11, the subgroup ID property is optional */ - gboolean has_subgroup = FALSE, is_sgid0 = FALSE, has_ext = FALSE, is_eog = FALSE, has_priority = FALSE, violation = FALSE; + gboolean has_subgroup = FALSE, is_sgid0 = FALSE, has_prop = FALSE, is_eog = FALSE, has_priority = FALSE, violation = FALSE; imquic_moq_data_message_type_to_subgroup_header(moq->version, dtype, - &has_subgroup, &is_sgid0, &has_ext, &is_eog, &has_priority, &violation); - IMQUIC_LOG(IMQUIC_LOG_HUGE, "[%s][MoQ] SUBGROUP_HEADER type %02x: sg=%d, sgid0=%d, ext=%d, eog=%d, pri=%d, viol=%d\n", - imquic_get_connection_name(moq->conn), dtype, has_subgroup, is_sgid0, has_ext, is_eog, has_priority, violation); + &has_subgroup, &is_sgid0, &has_prop, &is_eog, &has_priority, &violation); + IMQUIC_LOG(IMQUIC_LOG_HUGE, "[%s][MoQ] SUBGROUP_HEADER type %02x: sg=%d, sgid0=%d, prop=%d, eog=%d, pri=%d, viol=%d\n", + imquic_get_connection_name(moq->conn), dtype, has_subgroup, is_sgid0, has_prop, is_eog, has_priority, violation); IMQUIC_MOQ_CHECK_ERR(violation, error, IMQUIC_MOQ_PROTOCOL_VIOLATION, 0, "Invalid SUBGROUP_HEADER type"); if(has_subgroup) { - subgroup_id = imquic_read_varint(&bytes[offset], blen-offset, &length); + subgroup_id = imquic_read_moqint(moq->version, &bytes[offset], blen-offset, &length); IMQUIC_MOQ_CHECK_ERR(length == 0 || length >= blen-offset, NULL, 0, 0, "Broken SUBGROUP_HEADER"); offset += length; } else { @@ -4537,7 +3773,7 @@ size_t imquic_moq_parse_subgroup_header(imquic_moq_context *moq, imquic_moq_stre imquic_get_connection_name(moq->conn), priority); /* Track these properties */ if(moq_stream != NULL) { - moq_stream->request_id = 0; /* TODO remove? */ + moq_stream->request_id = 0; moq_stream->track_alias = track_alias; moq_stream->group_id = group_id; moq_stream->subgroup_id = subgroup_id; @@ -4564,41 +3800,40 @@ int imquic_moq_parse_subgroup_header_object(imquic_moq_context *moq, imquic_moq_ size_t blen = moq_stream->buffer->length; size_t offset = 0; uint8_t length = 0; - /* Note: this will be a delta, on v14 and later */ - uint64_t object_id = imquic_read_varint(&bytes[offset], blen-offset, &length); + uint64_t object_id = imquic_read_moqint(moq->version, &bytes[offset], blen-offset, &length); if(length == 0 || length >= blen-offset) return -1; /* Not enough data, try again later */ offset += length; - size_t ext_offset = 0, ext_len = 0; + size_t prop_offset = 0, prop_len = 0; /* TODO We can optimize this by only doing it once, when we parse the header */ /* TODO Check EOG too */ - gboolean has_subgroup = FALSE, is_sgid0 = FALSE, has_ext = FALSE, has_priority = FALSE; - imquic_moq_data_message_type_to_subgroup_header(moq->version, moq_stream->type, &has_subgroup, &is_sgid0, &has_ext, NULL, &has_priority, NULL); - if(has_ext) { - /* The object contains extensions */ - ext_len = imquic_read_varint(&bytes[offset], blen-offset, &length); + gboolean has_subgroup = FALSE, is_sgid0 = FALSE, has_prop = FALSE, has_priority = FALSE; + imquic_moq_data_message_type_to_subgroup_header(moq->version, moq_stream->type, &has_subgroup, &is_sgid0, &has_prop, NULL, &has_priority, NULL); + if(has_prop) { + /* The object contains properties */ + prop_len = imquic_read_moqint(moq->version, &bytes[offset], blen-offset, &length); if(length == 0 || length >= blen-offset) return -1; /* Not enough data, try again later */ offset += length; - IMQUIC_LOG(IMQUIC_MOQ_LOG_HUGE, "[%s][MoQ] -- Extensions Length: %"SCNu64"\n", - imquic_get_connection_name(moq->conn), ext_len); - ext_offset = offset; - if(length == 0 || ext_len >= blen-offset) + IMQUIC_LOG(IMQUIC_MOQ_LOG_HUGE, "[%s][MoQ] -- Properties Length: %"SCNu64"\n", + imquic_get_connection_name(moq->conn), prop_len); + prop_offset = offset; + if(length == 0 || prop_len >= blen-offset) return -1; /* Not enough data, try again later */ - offset += ext_len; + offset += prop_len; } - uint64_t p_len = imquic_read_varint(&bytes[offset], blen-offset, &length); + uint64_t p_len = imquic_read_moqint(moq->version, &bytes[offset], blen-offset, &length); if(length == 0 || length >= blen-offset) return -1; /* Not enough data, try again later */ offset += length; uint64_t object_status = 0; if(p_len == 0) { - object_status = imquic_read_varint(&bytes[offset], blen-offset, &length); + object_status = imquic_read_moqint(moq->version, &bytes[offset], blen-offset, &length); if(length == 0 || length > blen-offset) return -1; /* Not enough data, try again later */ /* TODO An invalid object status should be a protocol violation error */ //~ IMQUIC_MOQ_CHECK_ERR(object_status > IMQUIC_MOQ_END_OF_TRACK, error, IMQUIC_MOQ_PROTOCOL_VIOLATION, 0, "Invalid object status"); - //~ IMQUIC_MOQ_CHECK_ERR(object_status == IMQUIC_MOQ_OBJECT_DOESNT_EXIST && ext_len > 0, error, IMQUIC_MOQ_PROTOCOL_VIOLATION, 0, "Extensions received in object with status 'Does Not Exist'"); + //~ IMQUIC_MOQ_CHECK_ERR(object_status == IMQUIC_MOQ_OBJECT_DOESNT_EXIST && prop_len > 0, error, IMQUIC_MOQ_PROTOCOL_VIOLATION, 0, "Properties received in object with status 'Does Not Exist'"); offset += length; } if(p_len > blen-offset) @@ -4616,19 +3851,17 @@ int imquic_moq_parse_subgroup_header_object(imquic_moq_context *moq, imquic_moq_ * is set to the first object we receive in the sequence */ moq_stream->subgroup_id = object_id; } - if(moq->version >= IMQUIC_MOQ_VERSION_14) { - /* Object IDs are a delta, starting from v14 */ - object_id += moq_stream->last_object_id; - if(moq_stream->got_objects) - object_id++; - } + /* Object IDs are a delta */ + object_id += moq_stream->last_object_id; + if(moq_stream->got_objects) + object_id++; if(!moq_stream->got_objects) moq_stream->got_objects = TRUE; moq_stream->last_object_id = object_id; - GList *extensions = NULL; - if(ext_offset > 0 && ext_len > 0) { + GList *properties = NULL; + if(prop_offset > 0 && prop_len > 0) { /* TODO Check Protocol Violation cases */ - extensions = imquic_moq_parse_object_extensions(moq->version, &bytes[ext_offset], ext_len); + properties = imquic_moq_parse_properties(moq->version, &bytes[prop_offset], prop_len); } /* Notify the payload at the application layer */ imquic_moq_object object = { @@ -4641,7 +3874,7 @@ int imquic_moq_parse_subgroup_header_object(imquic_moq_context *moq, imquic_moq_ .priority = moq_stream->priority, .payload = bytes + offset, .payload_len = p_len, - .extensions = extensions, + .properties = properties, .delivery = IMQUIC_MOQ_USE_SUBGROUP, .end_of_stream = complete }; @@ -4651,7 +3884,7 @@ int imquic_moq_parse_subgroup_header_object(imquic_moq_context *moq, imquic_moq_ #endif if(moq->conn->socket && moq->conn->socket->callbacks.moq.incoming_object) moq->conn->socket->callbacks.moq.incoming_object(moq->conn, &object); - g_list_free_full(extensions, (GDestroyNotify)imquic_moq_object_extension_free); + g_list_free_full(properties, (GDestroyNotify)imquic_moq_property_free); /* Move on */ offset += p_len; imquic_buffer_shift(moq_stream->buffer, offset); @@ -4668,7 +3901,7 @@ size_t imquic_moq_parse_fetch_header(imquic_moq_context *moq, imquic_moq_stream return 0; size_t offset = 0; uint8_t length = 0; - uint64_t request_id = imquic_read_varint(&bytes[offset], blen-offset, &length); + uint64_t request_id = imquic_read_moqint(moq->version, &bytes[offset], blen-offset, &length); IMQUIC_MOQ_CHECK_ERR(length == 0 || length > blen-offset, NULL, 0, 0, "Broken FETCH_HEADER"); offset += length; IMQUIC_LOG(IMQUIC_MOQ_LOG_HUGE, "[%s][MoQ] -- Request ID: %"SCNu64"\n", @@ -4700,26 +3933,20 @@ int imquic_moq_parse_fetch_header_object(imquic_moq_context *moq, imquic_moq_str uint8_t length = 0; /* Since v15, FETCH objects are prefixed by serialization flags * that are supposed to optimize what will and will not be there */ - uint64_t flags = 0; - if(moq->version == IMQUIC_MOQ_VERSION_15) { - flags = bytes[offset]; - offset++; - } else if(moq->version >= IMQUIC_MOQ_VERSION_16) { - flags = imquic_read_varint(&bytes[offset], blen-offset, &length); - if(length == 0 || length >= blen-offset) - return -1; /* Not enough data, try again later */ - offset += length; - } + uint64_t flags = imquic_read_moqint(moq->version, &bytes[offset], blen-offset, &length); + if(length == 0 || length >= blen-offset) + return -1; /* Not enough data, try again later */ + offset += length; if(length >= blen-offset) return -1; /* Not enough data, try again later */ imquic_moq_fetch_subgroup_type subgroup_type = IMQUIC_MOQ_FETCH_SUBGROUP_ID; - gboolean has_oid = FALSE, has_group = FALSE, has_priority = FALSE, has_ext = FALSE, + gboolean has_oid = FALSE, has_group = FALSE, has_priority = FALSE, has_prop = FALSE, is_datagram = FALSE, end_ne_range = FALSE, end_uk_range = FALSE, violation = FALSE; imquic_moq_parse_fetch_serialization_flags(moq->version, flags, - &subgroup_type, &has_oid, &has_group, &has_priority, &has_ext, &is_datagram, &end_ne_range, &end_uk_range, &violation); + &subgroup_type, &has_oid, &has_group, &has_priority, &has_prop, &is_datagram, &end_ne_range, &end_uk_range, &violation); uint64_t group_id = 0; if(has_group) { - group_id = imquic_read_varint(&bytes[offset], blen-offset, &length); + group_id = imquic_read_moqint(moq->version, &bytes[offset], blen-offset, &length); if(length == 0 || length >= blen-offset) return -1; /* Not enough data, try again later */ offset += length; @@ -4730,7 +3957,7 @@ int imquic_moq_parse_fetch_header_object(imquic_moq_context *moq, imquic_moq_str } uint64_t subgroup_id = 0; if(subgroup_type == IMQUIC_MOQ_FETCH_SUBGROUP_ID) { - subgroup_id = imquic_read_varint(&bytes[offset], blen-offset, &length); + subgroup_id = imquic_read_moqint(moq->version, &bytes[offset], blen-offset, &length); if(length == 0 || length >= blen-offset) return -1; /* Not enough data, try again later */ offset += length; @@ -4744,7 +3971,7 @@ int imquic_moq_parse_fetch_header_object(imquic_moq_context *moq, imquic_moq_str } uint64_t object_id = 0; if(has_oid) { - object_id = imquic_read_varint(&bytes[offset], blen-offset, &length); + object_id = imquic_read_moqint(moq->version, &bytes[offset], blen-offset, &length); if(length == 0 || length >= blen-offset) return -1; /* Not enough data, try again later */ offset += length; @@ -4762,31 +3989,31 @@ int imquic_moq_parse_fetch_header_object(imquic_moq_context *moq, imquic_moq_str IMQUIC_MOQ_CHECK_ERR(!moq_stream->got_objects, error, IMQUIC_MOQ_PROTOCOL_VIOLATION, -1, "Serialization flag references non-existing previous object"); priority = moq_stream->last_priority; } - size_t ext_offset = 0, ext_len = 0; - if(has_ext) { - ext_len = imquic_read_varint(&bytes[offset], blen-offset, &length); + size_t prop_offset = 0, prop_len = 0; + if(has_prop) { + prop_len = imquic_read_moqint(moq->version, &bytes[offset], blen-offset, &length); if(length == 0 || length >= blen-offset) return -1; /* Not enough data, try again later */ offset += length; - IMQUIC_LOG(IMQUIC_MOQ_LOG_HUGE, "[%s][MoQ] -- Extensions Length: %"SCNu64"\n", - imquic_get_connection_name(moq->conn), ext_len); - ext_offset = offset; - if(length == 0 || ext_len >= blen-offset) + IMQUIC_LOG(IMQUIC_MOQ_LOG_HUGE, "[%s][MoQ] -- Properties Length: %"SCNu64"\n", + imquic_get_connection_name(moq->conn), prop_len); + prop_offset = offset; + if(length == 0 || prop_len >= blen-offset) return -1; /* Not enough data, try again later */ - offset += ext_len; + offset += prop_len; } - uint64_t p_len = imquic_read_varint(&bytes[offset], blen-offset, &length); + uint64_t p_len = imquic_read_moqint(moq->version, &bytes[offset], blen-offset, &length); if(length == 0 || length >= blen-offset) return -1; /* Not enough data, try again later */ offset += length; uint64_t object_status = 0; if(p_len == 0) { - object_status = imquic_read_varint(&bytes[offset], blen-offset, &length); + object_status = imquic_read_moqint(moq->version, &bytes[offset], blen-offset, &length); if(length == 0 || length > blen-offset) return -1; /* Not enough data, try again later */ /* TODO An invalid object status should be a protocol violation error */ //~ IMQUIC_MOQ_CHECK_ERR(object_status > IMQUIC_MOQ_END_OF_TRACK, error, IMQUIC_MOQ_PROTOCOL_VIOLATION, 0, "Invalid object status"); - //~ IMQUIC_MOQ_CHECK_ERR(object_status == IMQUIC_MOQ_OBJECT_DOESNT_EXIST && ext_len > 0, error, IMQUIC_MOQ_PROTOCOL_VIOLATION, 0, "Extensions received in object with status 'Does Not Exist'"); + //~ IMQUIC_MOQ_CHECK_ERR(object_status == IMQUIC_MOQ_OBJECT_DOESNT_EXIST && prop_len > 0, error, IMQUIC_MOQ_PROTOCOL_VIOLATION, 0, "Properties received in object with status 'Does Not Exist'"); offset += length; } if(p_len > blen-offset) @@ -4809,10 +4036,10 @@ int imquic_moq_parse_fetch_header_object(imquic_moq_context *moq, imquic_moq_str moq_stream->last_subgroup_id = subgroup_id; moq_stream->last_object_id = object_id; moq_stream->last_priority = priority; - GList *extensions = NULL; - if(ext_offset > 0 && ext_len > 0) { + GList *properties = NULL; + if(prop_offset > 0 && prop_len > 0) { /* TODO Check Protocol Violation cases */ - extensions = imquic_moq_parse_object_extensions(moq->version, &bytes[ext_offset], ext_len); + properties = imquic_moq_parse_properties(moq->version, &bytes[prop_offset], prop_len); } /* Notify the payload at the application layer */ imquic_moq_object object = { @@ -4825,7 +4052,7 @@ int imquic_moq_parse_fetch_header_object(imquic_moq_context *moq, imquic_moq_str .priority = priority, .payload = bytes + offset, .payload_len = p_len, - .extensions = extensions, + .properties = properties, .delivery = IMQUIC_MOQ_USE_FETCH, .end_of_stream = complete }; @@ -4835,7 +4062,7 @@ int imquic_moq_parse_fetch_header_object(imquic_moq_context *moq, imquic_moq_str #endif if(moq->conn->socket && moq->conn->socket->callbacks.moq.incoming_object) moq->conn->socket->callbacks.moq.incoming_object(moq->conn, &object); - g_list_free_full(extensions, (GDestroyNotify)imquic_moq_object_extension_free); + g_list_free_full(properties, (GDestroyNotify)imquic_moq_property_free); /* Move on */ offset += p_len; imquic_buffer_shift(moq_stream->buffer, offset); @@ -4852,7 +4079,7 @@ size_t imquic_moq_parse_goaway(imquic_moq_context *moq, uint8_t *bytes, size_t b return 0; size_t offset = 0; uint8_t length = 0; - uint64_t uri_len = imquic_read_varint(&bytes[offset], blen-offset, &length); + uint64_t uri_len = imquic_read_moqint(moq->version, &bytes[offset], blen-offset, &length); IMQUIC_MOQ_CHECK_ERR(length == 0 || length > blen-offset, NULL, 0, 0, "Broken GOAWAY"); offset += length; char uri[8192], *uri_str = NULL; @@ -4870,18 +4097,26 @@ size_t imquic_moq_parse_goaway(imquic_moq_context *moq, uint8_t *bytes, size_t b } } offset += uri_len; + uint64_t timeout = 0; + if(moq->version >= IMQUIC_MOQ_VERSION_17) { + timeout = imquic_read_moqint(moq->version, &bytes[offset], blen-offset, &length); + IMQUIC_MOQ_CHECK_ERR(length == 0 || length > blen-offset, NULL, 0, 0, "Broken GOAWAY"); + offset += length; + } #ifdef HAVE_QLOG if(moq->conn->qlog != NULL && moq->conn->qlog->moq) { json_t *message = imquic_qlog_moq_message_prepare("goaway"); imquic_qlog_event_add_raw(message, "new_session_uri", (uint8_t *)uri_str, uri_len); - imquic_moq_qlog_control_message_parsed(moq->conn->qlog, moq->control_stream_id, bytes-3, offset+3, message); + if(moq->version >= IMQUIC_MOQ_VERSION_17) + json_object_set_new(message, "timeout", json_integer(timeout)); + imquic_moq_qlog_control_message_parsed(moq->conn->qlog, imquic_moq_get_control_stream(moq), bytes-3, offset+3, message); } #endif IMQUIC_MOQ_CHECK_ERR(!g_atomic_int_compare_and_exchange(&moq->got_goaway, 0, 1), error, IMQUIC_MOQ_PROTOCOL_VIOLATION, 0, "Multiple GOAWAY messages received"); /* Notify the application */ if(moq->conn->socket && moq->conn->socket->callbacks.moq.incoming_goaway) - moq->conn->socket->callbacks.moq.incoming_goaway(moq->conn, uri_str); + moq->conn->socket->callbacks.moq.incoming_goaway(moq->conn, uri_str, timeout); if(error) *error = 0; return offset; @@ -4889,45 +4124,22 @@ size_t imquic_moq_parse_goaway(imquic_moq_context *moq, uint8_t *bytes, size_t b /* Message building */ size_t imquic_moq_add_client_setup(imquic_moq_context *moq, uint8_t *bytes, size_t blen, - GList *supported_versions, imquic_moq_setup_parameters *parameters) { - if(bytes == NULL || blen < 4 || (g_list_length(supported_versions) < 1 && - (moq->version == IMQUIC_MOQ_VERSION_ANY_LEGACY || (moq->version >= IMQUIC_MOQ_VERSION_MIN && moq->version <= IMQUIC_MOQ_VERSION_14)))) { + imquic_moq_setup_options *options) { + if(bytes == NULL || blen < 2 || moq->version >= IMQUIC_MOQ_VERSION_17) { IMQUIC_LOG(IMQUIC_LOG_ERR, "[%s][MoQ] Can't add MoQ %s: invalid arguments\n", imquic_get_connection_name(moq->conn), imquic_moq_message_type_str(IMQUIC_MOQ_CLIENT_SETUP, moq->version)); return 0; } size_t offset = 0, len_offset = 0; IMQUIC_MOQ_ADD_MESSAGE_TYPE(IMQUIC_MOQ_CLIENT_SETUP); - /* Version is only negotiated here for versions up to v14 */ - if(moq->version == IMQUIC_MOQ_VERSION_ANY_LEGACY || (moq->version >= IMQUIC_MOQ_VERSION_MIN && moq->version <= IMQUIC_MOQ_VERSION_14)) { - offset += imquic_write_varint(g_list_length(supported_versions), &bytes[offset], blen-offset); - GList *temp = supported_versions; - while(temp) { - uint32_t version = GPOINTER_TO_UINT(temp->data); - offset += imquic_write_varint(version, &bytes[offset], blen-offset); - temp = temp->next; - } - } - uint8_t params_num = 0; - offset += imquic_moq_setup_parameters_serialize(moq, parameters, &bytes[offset], blen-offset, ¶ms_num); + uint8_t opts_num = 0; + offset += imquic_moq_setup_options_serialize(moq, options, &bytes[offset], blen-offset, &opts_num); IMQUIC_MOQ_ADD_MESSAGE_LENGTH(); #ifdef HAVE_QLOG if(moq->conn->qlog != NULL && moq->conn->qlog->moq) { json_t *message = imquic_qlog_moq_message_prepare("client_setup"); - if(moq->version == IMQUIC_MOQ_VERSION_ANY_LEGACY || (moq->version >= IMQUIC_MOQ_VERSION_MIN && moq->version <= IMQUIC_MOQ_VERSION_14)) { - json_object_set_new(message, "number_of_supported_versions", json_integer(g_list_length(supported_versions))); - json_t *versions = json_array(); - GList *temp = supported_versions; - temp = supported_versions; - while(temp) { - uint32_t version = GPOINTER_TO_UINT(temp->data); - json_array_append_new(versions, json_integer(version)); - temp = temp->next; - } - json_object_set_new(message, "supported_versions", versions); - } - json_object_set_new(message, "number_of_parameters", json_integer(params_num)); - imquic_qlog_moq_message_add_setup_parameters(message, parameters, "setup_parameters"); + json_object_set_new(message, "number_of_options", json_integer(opts_num)); + imquic_qlog_moq_message_add_setup_options(message, options, "setup_options"); imquic_moq_qlog_control_message_created(moq->conn->qlog, moq->control_stream_id, bytes, offset, message); } #endif @@ -4935,27 +4147,44 @@ size_t imquic_moq_add_client_setup(imquic_moq_context *moq, uint8_t *bytes, size } size_t imquic_moq_add_server_setup(imquic_moq_context *moq, uint8_t *bytes, size_t blen, - uint32_t version, imquic_moq_setup_parameters *parameters) { - if(bytes == NULL || blen < 1) { + imquic_moq_setup_options *options) { + if(bytes == NULL || blen < 2 || moq->version >= IMQUIC_MOQ_VERSION_17) { IMQUIC_LOG(IMQUIC_LOG_ERR, "[%s][MoQ] Can't add MoQ %s: invalid arguments\n", imquic_get_connection_name(moq->conn), imquic_moq_message_type_str(IMQUIC_MOQ_SERVER_SETUP, moq->version)); return 0; } size_t offset = 0, len_offset = 0; IMQUIC_MOQ_ADD_MESSAGE_TYPE(IMQUIC_MOQ_SERVER_SETUP); - /* Version is only negotiated here for versions up to v14 */ - if(moq->version == IMQUIC_MOQ_VERSION_ANY_LEGACY || (moq->version >= IMQUIC_MOQ_VERSION_MIN && moq->version <= IMQUIC_MOQ_VERSION_14)) - offset += imquic_write_varint(version, &bytes[offset], blen-offset); - uint8_t params_num = 0; - offset += imquic_moq_setup_parameters_serialize(moq, parameters, &bytes[offset], blen-offset, ¶ms_num); + uint8_t opts_num = 0; + offset += imquic_moq_setup_options_serialize(moq, options, &bytes[offset], blen-offset, &opts_num); + IMQUIC_MOQ_ADD_MESSAGE_LENGTH(); +#ifdef HAVE_QLOG + if(moq->conn->qlog != NULL && moq->conn->qlog->moq) { + json_t *message = imquic_qlog_moq_message_prepare("setup"); + imquic_qlog_moq_message_add_setup_options(message, options, "setup_options"); + imquic_moq_qlog_control_message_created(moq->conn->qlog, moq->control_stream_id, bytes, offset, message); + } +#endif + return offset; +} + +size_t imquic_moq_add_setup(imquic_moq_context *moq, uint8_t *bytes, size_t blen, + imquic_moq_setup_options *options) { + if(bytes == NULL || blen < 2 || moq->version <= IMQUIC_MOQ_VERSION_16) { + IMQUIC_LOG(IMQUIC_LOG_ERR, "[%s][MoQ] Can't add MoQ %s: invalid arguments\n", + imquic_get_connection_name(moq->conn), imquic_moq_message_type_str(IMQUIC_MOQ_SETUP, moq->version)); + return 0; + } + size_t offset = 0, len_offset = 0; + IMQUIC_MOQ_ADD_MESSAGE_TYPE(IMQUIC_MOQ_SETUP); + uint8_t opts_num = 0; + offset += imquic_moq_setup_options_serialize(moq, options, &bytes[offset], blen-offset, &opts_num); IMQUIC_MOQ_ADD_MESSAGE_LENGTH(); #ifdef HAVE_QLOG if(moq->conn->qlog != NULL && moq->conn->qlog->moq) { json_t *message = imquic_qlog_moq_message_prepare("server_setup"); - if(moq->version == IMQUIC_MOQ_VERSION_ANY_LEGACY || (moq->version >= IMQUIC_MOQ_VERSION_MIN && moq->version <= IMQUIC_MOQ_VERSION_14)) - json_object_set_new(message, "selected_version", json_integer(version)); - json_object_set_new(message, "number_of_parameters", json_integer(params_num)); - imquic_qlog_moq_message_add_setup_parameters(message, parameters, "setup_parameters"); + json_object_set_new(message, "number_of_options", json_integer(opts_num)); + imquic_qlog_moq_message_add_setup_options(message, options, "setup_options"); imquic_moq_qlog_control_message_created(moq->conn->qlog, moq->control_stream_id, bytes, offset, message); } #endif @@ -4963,14 +4192,14 @@ size_t imquic_moq_add_server_setup(imquic_moq_context *moq, uint8_t *bytes, size } size_t imquic_moq_add_max_request_id(imquic_moq_context *moq, uint8_t *bytes, size_t blen, uint64_t max_request_id) { - if(bytes == NULL || blen < 1) { + if(bytes == NULL || blen < 1 || moq->version >= IMQUIC_MOQ_VERSION_17) { IMQUIC_LOG(IMQUIC_LOG_ERR, "[%s][MoQ] Can't add MoQ %s: invalid arguments\n", imquic_get_connection_name(moq->conn), imquic_moq_message_type_str(IMQUIC_MOQ_MAX_REQUEST_ID, moq->version)); return 0; } size_t offset = 0, len_offset = 0; IMQUIC_MOQ_ADD_MESSAGE_TYPE(IMQUIC_MOQ_MAX_REQUEST_ID); - offset += imquic_write_varint(max_request_id, &bytes[offset], blen-offset); + offset += imquic_write_moqint(moq->version, max_request_id, &bytes[offset], blen-offset); IMQUIC_MOQ_ADD_MESSAGE_LENGTH(); #ifdef HAVE_QLOG if(moq->conn->qlog != NULL && moq->conn->qlog->moq) { @@ -4983,14 +4212,14 @@ size_t imquic_moq_add_max_request_id(imquic_moq_context *moq, uint8_t *bytes, si } size_t imquic_moq_add_requests_blocked(imquic_moq_context *moq, uint8_t *bytes, size_t blen, uint64_t max_request_id) { - if(bytes == NULL || blen < 1) { + if(bytes == NULL || blen < 1 || moq->version >= IMQUIC_MOQ_VERSION_17) { IMQUIC_LOG(IMQUIC_LOG_ERR, "[%s][MoQ] Can't add MoQ %s: invalid arguments\n", imquic_get_connection_name(moq->conn), imquic_moq_message_type_str(IMQUIC_MOQ_REQUESTS_BLOCKED, moq->version)); return 0; } size_t offset = 0, len_offset = 0; IMQUIC_MOQ_ADD_MESSAGE_TYPE(IMQUIC_MOQ_REQUESTS_BLOCKED); - offset += imquic_write_varint(max_request_id, &bytes[offset], blen-offset); + offset += imquic_write_moqint(moq->version, max_request_id, &bytes[offset], blen-offset); IMQUIC_MOQ_ADD_MESSAGE_LENGTH(); #ifdef HAVE_QLOG if(moq->conn->qlog != NULL && moq->conn->qlog->moq) { @@ -5004,21 +4233,23 @@ size_t imquic_moq_add_requests_blocked(imquic_moq_context *moq, uint8_t *bytes, size_t imquic_moq_add_request_ok(imquic_moq_context *moq, imquic_moq_stream *moq_stream, uint8_t *bytes, size_t blen, uint64_t request_id, imquic_moq_request_parameters *parameters) { - if(bytes == NULL || blen < 1) { + if(bytes == NULL || blen < 1 || (moq->version >= IMQUIC_MOQ_VERSION_17 && moq_stream == NULL)) { IMQUIC_LOG(IMQUIC_LOG_ERR, "[%s][MoQ] Can't add MoQ %s: invalid arguments\n", imquic_get_connection_name(moq->conn), imquic_moq_message_type_str(IMQUIC_MOQ_REQUEST_OK, moq->version)); return 0; } size_t offset = 0, len_offset = 0; IMQUIC_MOQ_ADD_MESSAGE_TYPE(IMQUIC_MOQ_REQUEST_OK); - offset += imquic_write_varint(request_id, &bytes[offset], blen-offset); + if(moq->version <= IMQUIC_MOQ_VERSION_16) + offset += imquic_write_moqint(moq->version, request_id, &bytes[offset], blen-offset); uint8_t params_num = 0; - offset += imquic_moq_request_parameters_serialize(moq, parameters, &bytes[offset], blen-offset, ¶ms_num); + offset += imquic_moq_request_parameters_serialize(moq, IMQUIC_MOQ_REQUEST_OK, parameters, &bytes[offset], blen-offset, ¶ms_num); IMQUIC_MOQ_ADD_MESSAGE_LENGTH(); #ifdef HAVE_QLOG if(moq->conn->qlog != NULL && moq->conn->qlog->moq) { json_t *message = imquic_qlog_moq_message_prepare("request_ok"); - json_object_set_new(message, "request_id", json_integer(request_id)); + if(moq->version <= IMQUIC_MOQ_VERSION_16) + json_object_set_new(message, "request_id", json_integer(request_id)); json_object_set_new(message, "number_of_parameters", json_integer(params_num)); imquic_qlog_moq_message_add_request_parameters(message, moq->version, parameters, "parameters"); imquic_moq_qlog_control_message_created(moq->conn->qlog, @@ -5030,19 +4261,20 @@ size_t imquic_moq_add_request_ok(imquic_moq_context *moq, imquic_moq_stream *moq size_t imquic_moq_add_request_error(imquic_moq_context *moq, imquic_moq_stream *moq_stream, uint8_t *bytes, size_t blen, uint64_t request_id, uint64_t error, const char *reason, uint64_t retry_interval) { - if(bytes == NULL || blen < 1 || (reason && strlen(reason) > 1024)) { + if(bytes == NULL || blen < 1 || (reason && strlen(reason) > 1024) || + (moq->version >= IMQUIC_MOQ_VERSION_17 && moq_stream == NULL)) { IMQUIC_LOG(IMQUIC_LOG_ERR, "[%s][MoQ] Can't add MoQ %s: invalid arguments\n", imquic_get_connection_name(moq->conn), imquic_moq_message_type_str(IMQUIC_MOQ_REQUEST_ERROR, moq->version)); return 0; } size_t offset = 0, len_offset = 0; IMQUIC_MOQ_ADD_MESSAGE_TYPE(IMQUIC_MOQ_REQUEST_ERROR); - offset += imquic_write_varint(request_id, &bytes[offset], blen-offset); - offset += imquic_write_varint(error, &bytes[offset], blen-offset); - if(moq->version >= IMQUIC_MOQ_VERSION_16) - offset += imquic_write_varint(retry_interval, &bytes[offset], blen-offset); + if(moq->version <= IMQUIC_MOQ_VERSION_16) + offset += imquic_write_moqint(moq->version, request_id, &bytes[offset], blen-offset); + offset += imquic_write_moqint(moq->version, error, &bytes[offset], blen-offset); + offset += imquic_write_moqint(moq->version, retry_interval, &bytes[offset], blen-offset); size_t reason_len = reason ? strlen(reason) : 0; - offset += imquic_write_varint(reason_len, &bytes[offset], blen-offset); + offset += imquic_write_moqint(moq->version, reason_len, &bytes[offset], blen-offset); if(reason_len > 0) { memcpy(&bytes[offset], reason, reason_len); offset += reason_len; @@ -5051,10 +4283,10 @@ size_t imquic_moq_add_request_error(imquic_moq_context *moq, imquic_moq_stream * #ifdef HAVE_QLOG if(moq->conn->qlog != NULL && moq->conn->qlog->moq) { json_t *message = imquic_qlog_moq_message_prepare("request_error"); - json_object_set_new(message, "request_id", json_integer(request_id)); + if(moq->version <= IMQUIC_MOQ_VERSION_16) + json_object_set_new(message, "request_id", json_integer(request_id)); json_object_set_new(message, "error_code", json_integer(error)); - if(moq->version >= IMQUIC_MOQ_VERSION_16) - json_object_set_new(message, "retry_interval", json_integer(retry_interval)); + json_object_set_new(message, "retry_interval", json_integer(retry_interval)); if(reason != NULL) json_object_set_new(message, "reason", json_string(reason)); imquic_moq_qlog_control_message_created(moq->conn->qlog, @@ -5064,100 +4296,55 @@ size_t imquic_moq_add_request_error(imquic_moq_context *moq, imquic_moq_stream * return offset; } -size_t imquic_moq_add_publish_namespace(imquic_moq_context *moq, uint8_t *bytes, size_t blen, - uint64_t request_id, imquic_moq_namespace *track_namespace, imquic_moq_request_parameters *parameters) { - if(bytes == NULL || blen < 1 || track_namespace == NULL) { +size_t imquic_moq_add_publish_namespace(imquic_moq_context *moq, imquic_moq_stream *moq_stream, uint8_t *bytes, size_t blen, + uint64_t request_id, uint64_t required_id_delta, imquic_moq_namespace *track_namespace, imquic_moq_request_parameters *parameters) { + if(bytes == NULL || blen < 1 || track_namespace == NULL || + (moq->version >= IMQUIC_MOQ_VERSION_17 && moq_stream == NULL)) { IMQUIC_LOG(IMQUIC_LOG_ERR, "[%s][MoQ] Can't add MoQ %s: invalid arguments\n", imquic_get_connection_name(moq->conn), imquic_moq_message_type_str(IMQUIC_MOQ_PUBLISH_NAMESPACE, moq->version)); return 0; } size_t offset = 0, len_offset = 0; IMQUIC_MOQ_ADD_MESSAGE_TYPE(IMQUIC_MOQ_PUBLISH_NAMESPACE); - offset += imquic_write_varint(request_id, &bytes[offset], blen-offset); + offset += imquic_write_moqint(moq->version, request_id, &bytes[offset], blen-offset); + if(moq->version >= IMQUIC_MOQ_VERSION_17) + offset += imquic_write_moqint(moq->version, required_id_delta, &bytes[offset], blen-offset); IMQUIC_MOQ_ADD_NAMESPACES(IMQUIC_MOQ_PUBLISH_NAMESPACE); uint8_t params_num = 0; - offset += imquic_moq_request_parameters_serialize(moq, parameters, &bytes[offset], blen-offset, ¶ms_num); + offset += imquic_moq_request_parameters_serialize(moq, IMQUIC_MOQ_PUBLISH_NAMESPACE, parameters, &bytes[offset], blen-offset, ¶ms_num); IMQUIC_MOQ_ADD_MESSAGE_LENGTH(); #ifdef HAVE_QLOG if(moq->conn->qlog != NULL && moq->conn->qlog->moq) { + if(moq_stream != NULL) + imquic_moq_qlog_stream_type_set(moq->conn->qlog, TRUE, moq_stream->stream_id, "publish_namespace"); json_t *message = imquic_qlog_moq_message_prepare("publish_namespace"); json_object_set_new(message, "request_id", json_integer(request_id)); + if(moq->version >= IMQUIC_MOQ_VERSION_17) + json_object_set_new(message, "required_request_id_delta", json_integer(required_id_delta)); imquic_qlog_moq_message_add_namespace(message, track_namespace, "track_namespace"); json_object_set_new(message, "number_of_parameters", json_integer(params_num)); imquic_qlog_moq_message_add_request_parameters(message, moq->version, parameters, "parameters"); - imquic_moq_qlog_control_message_created(moq->conn->qlog, moq->control_stream_id, bytes, offset, message); - } -#endif - return offset; -} - -size_t imquic_moq_add_publish_namespace_ok(imquic_moq_context *moq, uint8_t *bytes, size_t blen, uint64_t request_id) { - if(bytes == NULL || blen < 1) { - IMQUIC_LOG(IMQUIC_LOG_ERR, "[%s][MoQ] Can't add MoQ %s: invalid arguments\n", - imquic_get_connection_name(moq->conn), imquic_moq_message_type_str(IMQUIC_MOQ_PUBLISH_NAMESPACE_OK, moq->version)); - return 0; - } - size_t offset = 0, len_offset = 0; - IMQUIC_MOQ_ADD_MESSAGE_TYPE(IMQUIC_MOQ_PUBLISH_NAMESPACE_OK); - offset += imquic_write_varint(request_id, &bytes[offset], blen-offset); - IMQUIC_MOQ_ADD_MESSAGE_LENGTH(); -#ifdef HAVE_QLOG - if(moq->conn->qlog != NULL && moq->conn->qlog->moq) { - json_t *message = imquic_qlog_moq_message_prepare(moq->version >= IMQUIC_MOQ_VERSION_15 ? "request_ok" : "publish_namespace_ok"); - json_object_set_new(message, "request_id", json_integer(request_id)); - imquic_moq_qlog_control_message_created(moq->conn->qlog, moq->control_stream_id, bytes, offset, message); - } -#endif - return offset; -} - -size_t imquic_moq_add_publish_namespace_error(imquic_moq_context *moq, uint8_t *bytes, size_t blen, - uint64_t request_id, imquic_moq_request_error_code error, const char *reason) { - if(bytes == NULL || blen < 1 || (reason && strlen(reason) > 1024)) { - IMQUIC_LOG(IMQUIC_LOG_ERR, "[%s][MoQ] Can't add MoQ %s: invalid arguments\n", - imquic_get_connection_name(moq->conn), imquic_moq_message_type_str(IMQUIC_MOQ_PUBLISH_NAMESPACE_ERROR, moq->version)); - return 0; - } - if(moq->version < IMQUIC_MOQ_VERSION_15) - error = (imquic_moq_request_error_code)imquic_moq_request_error_code_to_legacy(moq->version, error); - size_t offset = 0, len_offset = 0; - IMQUIC_MOQ_ADD_MESSAGE_TYPE(IMQUIC_MOQ_PUBLISH_NAMESPACE_ERROR); - offset += imquic_write_varint(request_id, &bytes[offset], blen-offset); - offset += imquic_write_varint(error, &bytes[offset], blen-offset); - size_t reason_len = reason ? strlen(reason) : 0; - offset += imquic_write_varint(reason_len, &bytes[offset], blen-offset); - if(reason_len > 0) { - memcpy(&bytes[offset], reason, reason_len); - offset += reason_len; - } - IMQUIC_MOQ_ADD_MESSAGE_LENGTH(); -#ifdef HAVE_QLOG - if(moq->conn->qlog != NULL && moq->conn->qlog->moq) { - json_t *message = imquic_qlog_moq_message_prepare(moq->version >= IMQUIC_MOQ_VERSION_15 ? "request_error" : "publish_namespace_error"); - json_object_set_new(message, "request_id", json_integer(request_id)); - json_object_set_new(message, "error_code", json_integer(error)); - if(reason != NULL) - json_object_set_new(message, "reason", json_string(reason)); - imquic_moq_qlog_control_message_created(moq->conn->qlog, moq->control_stream_id, bytes, offset, message); + imquic_moq_qlog_control_message_created(moq->conn->qlog, + (moq_stream ? moq_stream->stream_id : moq->control_stream_id), bytes, offset, message); } #endif return offset; } -size_t imquic_moq_add_publish_namespace_done(imquic_moq_context *moq, uint8_t *bytes, size_t blen, imquic_moq_namespace *track_namespace) { - if(bytes == NULL || blen < 1 || track_namespace == NULL) { +size_t imquic_moq_add_publish_namespace_done(imquic_moq_context *moq, uint8_t *bytes, size_t blen, uint64_t request_id) { + if(bytes == NULL || blen < 1 || moq->version >= IMQUIC_MOQ_VERSION_17) { IMQUIC_LOG(IMQUIC_LOG_ERR, "[%s][MoQ] Can't add MoQ %s: invalid arguments\n", imquic_get_connection_name(moq->conn), imquic_moq_message_type_str(IMQUIC_MOQ_PUBLISH_NAMESPACE_DONE, moq->version)); return 0; } size_t offset = 0, len_offset = 0; IMQUIC_MOQ_ADD_MESSAGE_TYPE(IMQUIC_MOQ_PUBLISH_NAMESPACE_DONE); - IMQUIC_MOQ_ADD_NAMESPACES(IMQUIC_MOQ_PUBLISH_NAMESPACE_DONE); + offset += imquic_write_moqint(moq->version, request_id, &bytes[offset], blen-offset); IMQUIC_MOQ_ADD_MESSAGE_LENGTH(); #ifdef HAVE_QLOG if(moq->conn->qlog != NULL && moq->conn->qlog->moq) { json_t *message = imquic_qlog_moq_message_prepare("publish_namespace_done"); - imquic_qlog_moq_message_add_namespace(message, track_namespace, "track_namespace"); + json_object_set_new(message, "request_id", json_integer(request_id)); imquic_moq_qlog_control_message_created(moq->conn->qlog, moq->control_stream_id, bytes, offset, message); } #endif @@ -5165,20 +4352,18 @@ size_t imquic_moq_add_publish_namespace_done(imquic_moq_context *moq, uint8_t *b } size_t imquic_moq_add_publish_namespace_cancel(imquic_moq_context *moq, uint8_t *bytes, size_t blen, - imquic_moq_namespace *track_namespace, imquic_moq_request_error_code error, const char *reason) { - if(bytes == NULL || blen < 1 || track_namespace == NULL || (reason && strlen(reason) > 1024)) { + uint64_t request_id, imquic_moq_request_error_code error, const char *reason) { + if(bytes == NULL || blen < 1 || (reason && strlen(reason) > 1024) || moq->version >= IMQUIC_MOQ_VERSION_17) { IMQUIC_LOG(IMQUIC_LOG_ERR, "[%s][MoQ] Can't add MoQ %s: invalid arguments\n", imquic_get_connection_name(moq->conn), imquic_moq_message_type_str(IMQUIC_MOQ_PUBLISH_NAMESPACE_CANCEL, moq->version)); return 0; } - if(moq->version < IMQUIC_MOQ_VERSION_15) - error = (imquic_moq_request_error_code)imquic_moq_request_error_code_to_legacy(moq->version, error); size_t offset = 0, len_offset = 0; IMQUIC_MOQ_ADD_MESSAGE_TYPE(IMQUIC_MOQ_PUBLISH_NAMESPACE_CANCEL); - IMQUIC_MOQ_ADD_NAMESPACES(IMQUIC_MOQ_PUBLISH_NAMESPACE_CANCEL); - offset += imquic_write_varint(error, &bytes[offset], blen-offset); + offset += imquic_write_moqint(moq->version, request_id, &bytes[offset], blen-offset); + offset += imquic_write_moqint(moq->version, error, &bytes[offset], blen-offset); size_t reason_len = reason ? strlen(reason) : 0; - offset += imquic_write_varint(reason_len, &bytes[offset], blen-offset); + offset += imquic_write_moqint(moq->version, reason_len, &bytes[offset], blen-offset); if(reason_len > 0) { memcpy(&bytes[offset], reason, reason_len); offset += reason_len; @@ -5187,7 +4372,7 @@ size_t imquic_moq_add_publish_namespace_cancel(imquic_moq_context *moq, uint8_t #ifdef HAVE_QLOG if(moq->conn->qlog != NULL && moq->conn->qlog->moq) { json_t *message = imquic_qlog_moq_message_prepare("publish_namespace_cancel"); - imquic_qlog_moq_message_add_namespace(message, track_namespace, "track_namespace"); + json_object_set_new(message, "request_id", json_integer(request_id)); json_object_set_new(message, "error_code", json_integer(error)); if(reason != NULL) json_object_set_new(message, "reason", json_string(reason)); @@ -5197,377 +4382,201 @@ size_t imquic_moq_add_publish_namespace_cancel(imquic_moq_context *moq, uint8_t return offset; } -size_t imquic_moq_add_publish(imquic_moq_context *moq, uint8_t *bytes, size_t blen, uint64_t request_id, - imquic_moq_namespace *track_namespace, imquic_moq_name *track_name, uint64_t track_alias, imquic_moq_request_parameters *parameters, GList *track_extensions) { +size_t imquic_moq_add_publish(imquic_moq_context *moq, imquic_moq_stream *moq_stream, uint8_t *bytes, size_t blen, uint64_t request_id, uint64_t required_id_delta, + imquic_moq_namespace *track_namespace, imquic_moq_name *track_name, uint64_t track_alias, imquic_moq_request_parameters *parameters, GList *track_properties) { if(bytes == NULL || blen < 1 || track_namespace == NULL || track_name == NULL || (track_name->buffer == NULL && track_name->length > 0) || - (moq->version <= IMQUIC_MOQ_VERSION_14 && parameters == NULL)) { + (moq->version >= IMQUIC_MOQ_VERSION_17 && moq_stream == NULL)) { IMQUIC_LOG(IMQUIC_LOG_ERR, "[%s][MoQ] Can't add MoQ %s: invalid arguments\n", imquic_get_connection_name(moq->conn), imquic_moq_message_type_str(IMQUIC_MOQ_PUBLISH, moq->version)); return 0; } size_t offset = 0, len_offset = 0; IMQUIC_MOQ_ADD_MESSAGE_TYPE(IMQUIC_MOQ_PUBLISH); - offset += imquic_write_varint(request_id, &bytes[offset], blen-offset); + offset += imquic_write_moqint(moq->version, request_id, &bytes[offset], blen-offset); + if(moq->version >= IMQUIC_MOQ_VERSION_17) + offset += imquic_write_moqint(moq->version, required_id_delta, &bytes[offset], blen-offset); IMQUIC_MOQ_ADD_NAMESPACES(IMQUIC_MOQ_PUBLISH); IMQUIC_MOQ_ADD_TRACKNAME(IMQUIC_MOQ_PUBLISH); - offset += imquic_write_varint(track_alias, &bytes[offset], blen-offset); - /* For versions older than v15, we need to add some attributes manually, - * but we'll read them from the parameters object the application passed */ - if(moq->version <= IMQUIC_MOQ_VERSION_14) { - bytes[offset] = parameters->group_order; - offset++; - bytes[offset] = parameters->largest_object_set; - offset++; - if(parameters->largest_object_set) { - offset += imquic_write_varint(parameters->largest_object.group, &bytes[offset], blen-offset); - offset += imquic_write_varint(parameters->largest_object.object, &bytes[offset], blen-offset); - } - bytes[offset] = parameters->forward; - offset++; - } + offset += imquic_write_moqint(moq->version, track_alias, &bytes[offset], blen-offset); uint8_t params_num = 0; - offset += imquic_moq_request_parameters_serialize(moq, parameters, &bytes[offset], blen-offset, ¶ms_num); - /* Check if there are extensions to encode */ - if(moq->version >= IMQUIC_MOQ_VERSION_16) { - uint8_t extensions[256]; - size_t extensions_len = 0; - extensions_len = imquic_moq_build_object_extensions(moq->version, track_extensions, extensions, sizeof(extensions)); - offset += imquic_moq_add_object_extensions(moq, &bytes[offset], blen-offset, extensions, extensions_len); - } + offset += imquic_moq_request_parameters_serialize(moq, IMQUIC_MOQ_PUBLISH, parameters, &bytes[offset], blen-offset, ¶ms_num); + /* Check if there are properties to encode */ + uint8_t properties[256]; + size_t properties_len = 0; + properties_len = imquic_moq_build_properties(moq->version, track_properties, properties, sizeof(properties)); + offset += imquic_moq_add_properties(moq, &bytes[offset], blen-offset, properties, properties_len); IMQUIC_MOQ_ADD_MESSAGE_LENGTH(); #ifdef HAVE_QLOG if(moq->conn != NULL && moq->conn->qlog != NULL && moq->conn->qlog->moq) { + if(moq_stream != NULL) + imquic_moq_qlog_stream_type_set(moq->conn->qlog, TRUE, moq_stream->stream_id, "publish"); json_t *message = imquic_qlog_moq_message_prepare("publish"); json_object_set_new(message, "request_id", json_integer(request_id)); + if(moq->version >= IMQUIC_MOQ_VERSION_17) + json_object_set_new(message, "required_request_id_delta", json_integer(required_id_delta)); imquic_qlog_moq_message_add_namespace(message, track_namespace, "track_namespace"); imquic_qlog_moq_message_add_track(message, track_name); json_object_set_new(message, "track_alias", json_integer(track_alias)); - if(moq->version <= IMQUIC_MOQ_VERSION_14) { - json_object_set_new(message, "group_order", json_integer(parameters->group_order)); - json_object_set_new(message, "content_exists", json_integer(parameters->largest_object_set)); - if(parameters->largest_object_set) { - json_object_set_new(message, "largest_group_id", json_integer(parameters->largest_object.group)); - json_object_set_new(message, "largest_object_id", json_integer(parameters->largest_object.object)); - } - json_object_set_new(message, "forward", json_integer(parameters->forward)); - } json_object_set_new(message, "number_of_parameters", json_integer(params_num)); imquic_qlog_moq_message_add_request_parameters(message, moq->version, parameters, "parameters"); - if(moq->version >= IMQUIC_MOQ_VERSION_16) - imquic_qlog_moq_message_add_extensions(message, track_extensions, "track_extensions"); - imquic_moq_qlog_control_message_created(moq->conn->qlog, moq->control_stream_id, bytes, offset, message); + imquic_qlog_moq_message_add_properties(message, track_properties, "track_properties"); + imquic_moq_qlog_control_message_created(moq->conn->qlog, + (moq_stream ? moq_stream->stream_id : moq->control_stream_id), bytes, offset, message); } #endif return offset; } -size_t imquic_moq_add_publish_ok(imquic_moq_context *moq, uint8_t *bytes, size_t blen, uint64_t request_id, +size_t imquic_moq_add_publish_ok(imquic_moq_context *moq, imquic_moq_stream *moq_stream, + uint8_t *bytes, size_t blen, uint64_t request_id, imquic_moq_request_parameters *parameters) { - if(bytes == NULL || blen < 1 || (moq->version <= IMQUIC_MOQ_VERSION_14 && parameters == NULL)) { + if(bytes == NULL || blen < 2 || (moq->version >= IMQUIC_MOQ_VERSION_17 && moq_stream == NULL)) { IMQUIC_LOG(IMQUIC_LOG_ERR, "[%s][MoQ] Can't add MoQ %s: invalid arguments\n", imquic_get_connection_name(moq->conn), imquic_moq_message_type_str(IMQUIC_MOQ_PUBLISH, moq->version)); return 0; } size_t offset = 0, len_offset = 0; IMQUIC_MOQ_ADD_MESSAGE_TYPE(IMQUIC_MOQ_PUBLISH_OK); - offset += imquic_write_varint(request_id, &bytes[offset], blen-offset); - /* For versions older than v15, we need to add some attributes manually, - * but we'll read them from the parameters object the application passed */ - if(moq->version <= IMQUIC_MOQ_VERSION_14) { - bytes[offset] = parameters->forward; - offset++; - bytes[offset] = parameters->subscriber_priority_set ? - parameters->subscriber_priority : 128; - offset++; - bytes[offset] = parameters->group_order; - offset++; - offset += imquic_write_varint(parameters->subscription_filter.type, &bytes[offset], blen-offset); - if(parameters->subscription_filter.type == IMQUIC_MOQ_FILTER_ABSOLUTE_START || - parameters->subscription_filter.type == IMQUIC_MOQ_FILTER_ABSOLUTE_RANGE) { - offset += imquic_write_varint(parameters->subscription_filter.start_location.group, &bytes[offset], blen-offset); - offset += imquic_write_varint(parameters->subscription_filter.start_location.object, &bytes[offset], blen-offset); - } - if(parameters->subscription_filter.type == IMQUIC_MOQ_FILTER_ABSOLUTE_RANGE) - offset += imquic_write_varint(parameters->subscription_filter.end_group, &bytes[offset], blen-offset); - } + if(moq->version <= IMQUIC_MOQ_VERSION_16) + offset += imquic_write_moqint(moq->version, request_id, &bytes[offset], blen-offset); uint8_t params_num = 0; - offset += imquic_moq_request_parameters_serialize(moq, parameters, &bytes[offset], blen-offset, ¶ms_num); + offset += imquic_moq_request_parameters_serialize(moq, IMQUIC_MOQ_PUBLISH_OK, parameters, &bytes[offset], blen-offset, ¶ms_num); IMQUIC_MOQ_ADD_MESSAGE_LENGTH(); #ifdef HAVE_QLOG if(moq->conn != NULL && moq->conn->qlog != NULL && moq->conn->qlog->moq) { json_t *message = imquic_qlog_moq_message_prepare("publish_ok"); - json_object_set_new(message, "request_id", json_integer(request_id)); - if(moq->version <= IMQUIC_MOQ_VERSION_14) { - json_object_set_new(message, "forward", json_integer(parameters->forward)); - json_object_set_new(message, "subscriber_priority", json_integer(parameters->subscriber_priority_set ? - parameters->subscriber_priority : 128)); - json_object_set_new(message, "group_order", json_integer(parameters->group_order)); - json_object_set_new(message, "filter_type", json_integer(parameters->subscription_filter.type)); - if(parameters->subscription_filter.type == IMQUIC_MOQ_FILTER_ABSOLUTE_START || parameters->subscription_filter.type == IMQUIC_MOQ_FILTER_ABSOLUTE_RANGE) { - json_object_set_new(message, "start_group", json_integer(parameters->subscription_filter.start_location.group)); - json_object_set_new(message, "start_object", json_integer(parameters->subscription_filter.start_location.object)); - } - } + if(moq->version <= IMQUIC_MOQ_VERSION_16) + json_object_set_new(message, "request_id", json_integer(request_id)); json_object_set_new(message, "number_of_parameters", json_integer(params_num)); imquic_qlog_moq_message_add_request_parameters(message, moq->version, parameters, "parameters"); - imquic_moq_qlog_control_message_created(moq->conn->qlog, moq->control_stream_id, bytes, offset, message); - } -#endif - return offset; -} - -size_t imquic_moq_add_publish_error(imquic_moq_context *moq, uint8_t *bytes, size_t blen, uint64_t request_id, - imquic_moq_request_error_code error, const char *reason) { - if(bytes == NULL || blen < 1 || (reason && strlen(reason) > 1024)) { - IMQUIC_LOG(IMQUIC_LOG_ERR, "[%s][MoQ] Can't add MoQ %s: invalid arguments\n", - imquic_get_connection_name(moq->conn), imquic_moq_message_type_str(IMQUIC_MOQ_PUBLISH_ERROR, moq->version)); - return 0; - } - if(moq->version < IMQUIC_MOQ_VERSION_15) - error = (imquic_moq_request_error_code)imquic_moq_request_error_code_to_legacy(moq->version, error); - size_t offset = 0, len_offset = 0; - IMQUIC_MOQ_ADD_MESSAGE_TYPE(IMQUIC_MOQ_PUBLISH_ERROR); - offset += imquic_write_varint(request_id, &bytes[offset], blen-offset); - offset += imquic_write_varint(error, &bytes[offset], blen-offset); - size_t reason_len = reason ? strlen(reason) : 0; - offset += imquic_write_varint(reason_len, &bytes[offset], blen-offset); - if(reason_len > 0) { - memcpy(&bytes[offset], reason, reason_len); - offset += reason_len; - } - IMQUIC_MOQ_ADD_MESSAGE_LENGTH(); -#ifdef HAVE_QLOG - if(moq->conn != NULL && moq->conn->qlog != NULL && moq->conn->qlog->moq) { - json_t *message = imquic_qlog_moq_message_prepare(moq->version >= IMQUIC_MOQ_VERSION_15 ? "request_error" : "subscribe_error"); - json_object_set_new(message, "request_id", json_integer(request_id)); - json_object_set_new(message, "error_code", json_integer(error)); - if(reason != NULL) - json_object_set_new(message, "reason", json_string(reason)); - imquic_moq_qlog_control_message_created(moq->conn->qlog, moq->control_stream_id, bytes, offset, message); + imquic_moq_qlog_control_message_created(moq->conn->qlog, + (moq_stream ? moq_stream->stream_id : moq->control_stream_id), bytes, offset, message); } #endif return offset; } -size_t imquic_moq_add_subscribe(imquic_moq_context *moq, uint8_t *bytes, size_t blen, uint64_t request_id, uint64_t track_alias, +size_t imquic_moq_add_subscribe(imquic_moq_context *moq, imquic_moq_stream *moq_stream, + uint8_t *bytes, size_t blen, uint64_t request_id, uint64_t required_id_delta, imquic_moq_namespace *track_namespace, imquic_moq_name *track_name, imquic_moq_request_parameters *parameters) { if(bytes == NULL || blen < 1 || track_namespace == NULL || - track_name == NULL || (track_name->buffer == NULL && track_name->length > 0)) { + track_name == NULL || (track_name->buffer == NULL && track_name->length > 0) || + (moq->version >= IMQUIC_MOQ_VERSION_17 && moq_stream == NULL)) { IMQUIC_LOG(IMQUIC_LOG_ERR, "[%s][MoQ] Can't add MoQ %s: invalid arguments\n", imquic_get_connection_name(moq->conn), imquic_moq_message_type_str(IMQUIC_MOQ_SUBSCRIBE, moq->version)); return 0; } size_t offset = 0, len_offset = 0; IMQUIC_MOQ_ADD_MESSAGE_TYPE(IMQUIC_MOQ_SUBSCRIBE); - offset += imquic_write_varint(request_id, &bytes[offset], blen-offset); - if(moq->version < IMQUIC_MOQ_VERSION_12) - offset += imquic_write_varint(track_alias, &bytes[offset], blen-offset); + offset += imquic_write_moqint(moq->version, request_id, &bytes[offset], blen-offset); + if(moq->version >= IMQUIC_MOQ_VERSION_17) + offset += imquic_write_moqint(moq->version, required_id_delta, &bytes[offset], blen-offset); IMQUIC_MOQ_ADD_NAMESPACES(IMQUIC_MOQ_SUBSCRIBE); IMQUIC_MOQ_ADD_TRACKNAME(IMQUIC_MOQ_SUBSCRIBE); - /* For versions older than v15, we need to add some attributes manually, - * but we'll read them from the parameters object the application passed */ - if(moq->version <= IMQUIC_MOQ_VERSION_14) { - bytes[offset] = parameters->subscriber_priority_set ? - parameters->subscriber_priority : 128; - offset++; - bytes[offset] = parameters->group_order; - offset++; - bytes[offset] = parameters->forward; - offset++; - offset += imquic_write_varint(parameters->subscription_filter.type, &bytes[offset], blen-offset); - if(parameters->subscription_filter.type == IMQUIC_MOQ_FILTER_ABSOLUTE_START || - parameters->subscription_filter.type == IMQUIC_MOQ_FILTER_ABSOLUTE_RANGE) { - offset += imquic_write_varint(parameters->subscription_filter.start_location.group, &bytes[offset], blen-offset); - offset += imquic_write_varint(parameters->subscription_filter.start_location.object, &bytes[offset], blen-offset); - } - if(parameters->subscription_filter.type == IMQUIC_MOQ_FILTER_ABSOLUTE_RANGE) - offset += imquic_write_varint(parameters->subscription_filter.end_group, &bytes[offset], blen-offset); - } uint8_t params_num = 0; - offset += imquic_moq_request_parameters_serialize(moq, parameters, &bytes[offset], blen-offset, ¶ms_num); + offset += imquic_moq_request_parameters_serialize(moq, IMQUIC_MOQ_SUBSCRIBE, parameters, &bytes[offset], blen-offset, ¶ms_num); IMQUIC_MOQ_ADD_MESSAGE_LENGTH(); #ifdef HAVE_QLOG if(moq->conn->qlog != NULL && moq->conn->qlog->moq) { + if(moq_stream != NULL) + imquic_moq_qlog_stream_type_set(moq->conn->qlog, TRUE, moq_stream->stream_id, "subscribe"); json_t *message = imquic_qlog_moq_message_prepare("subscribe"); json_object_set_new(message, "request_id", json_integer(request_id)); - if(moq->version < IMQUIC_MOQ_VERSION_12) - json_object_set_new(message, "track_alias", json_integer(track_alias)); + if(moq->version >= IMQUIC_MOQ_VERSION_17) + json_object_set_new(message, "required_request_id_delta", json_integer(required_id_delta)); imquic_qlog_moq_message_add_namespace(message, track_namespace, "track_namespace"); imquic_qlog_moq_message_add_track(message, track_name); - if(moq->version <= IMQUIC_MOQ_VERSION_14) { - json_object_set_new(message, "subscriber_priority", json_integer(parameters->subscriber_priority_set ? - parameters->subscriber_priority : 128)); - json_object_set_new(message, "group_order", json_integer(parameters->group_order)); - json_object_set_new(message, "forward", json_integer(parameters->forward)); - json_object_set_new(message, "filter_type", json_integer(parameters->subscription_filter.type)); - if(parameters->subscription_filter.type == IMQUIC_MOQ_FILTER_ABSOLUTE_START || parameters->subscription_filter.type == IMQUIC_MOQ_FILTER_ABSOLUTE_RANGE) { - json_object_set_new(message, "start_group", json_integer(parameters->subscription_filter.start_location.group)); - json_object_set_new(message, "start_object", json_integer(parameters->subscription_filter.start_location.object)); - } - } json_object_set_new(message, "number_of_parameters", json_integer(params_num)); imquic_qlog_moq_message_add_request_parameters(message, moq->version, parameters, "parameters"); - imquic_moq_qlog_control_message_created(moq->conn->qlog, moq->control_stream_id, bytes, offset, message); + imquic_moq_qlog_control_message_created(moq->conn->qlog, + (moq_stream ? moq_stream->stream_id : moq->control_stream_id), bytes, offset, message); } #endif return offset; } -size_t imquic_moq_add_request_update(imquic_moq_context *moq, uint8_t *bytes, size_t blen, - uint64_t request_id, uint64_t sub_request_id, imquic_moq_request_parameters *parameters) { - if(bytes == NULL || blen < 1) { +size_t imquic_moq_add_request_update(imquic_moq_context *moq, imquic_moq_stream *moq_stream, uint8_t *bytes, size_t blen, + uint64_t request_id, uint64_t sub_request_id, uint64_t required_id_delta, imquic_moq_request_parameters *parameters) { + if(bytes == NULL || blen < 1 || (moq->version >= IMQUIC_MOQ_VERSION_17 && moq_stream == NULL)) { IMQUIC_LOG(IMQUIC_LOG_ERR, "[%s][MoQ] Can't add MoQ %s: invalid arguments\n", imquic_get_connection_name(moq->conn), imquic_moq_message_type_str(IMQUIC_MOQ_REQUEST_UPDATE, moq->version)); return 0; } size_t offset = 0, len_offset = 0; IMQUIC_MOQ_ADD_MESSAGE_TYPE(IMQUIC_MOQ_REQUEST_UPDATE); - offset += imquic_write_varint(request_id, &bytes[offset], blen-offset); - if(moq->version >= IMQUIC_MOQ_VERSION_14) - offset += imquic_write_varint(sub_request_id, &bytes[offset], blen-offset); - /* For versions older than v15, we need to add some attributes manually, - * but we'll read them from the parameters object the application passed */ - if(moq->version <= IMQUIC_MOQ_VERSION_14) { - offset += imquic_write_varint(parameters->subscription_filter.start_location.group, &bytes[offset], blen-offset); - offset += imquic_write_varint(parameters->subscription_filter.start_location.object, &bytes[offset], blen-offset); - offset += imquic_write_varint(parameters->subscription_filter.end_group, &bytes[offset], blen-offset); - bytes[offset] = parameters->subscriber_priority_set ? - parameters->subscriber_priority : 128; - offset++; - bytes[offset] = parameters->forward; - offset++; + offset += imquic_write_moqint(moq->version, request_id, &bytes[offset], blen-offset); + if(moq->version <= IMQUIC_MOQ_VERSION_16) { + offset += imquic_write_moqint(moq->version, sub_request_id, &bytes[offset], blen-offset); + } else { + offset += imquic_write_moqint(moq->version, required_id_delta, &bytes[offset], blen-offset); } uint8_t params_num = 0; - offset += imquic_moq_request_parameters_serialize(moq, parameters, &bytes[offset], blen-offset, ¶ms_num); + offset += imquic_moq_request_parameters_serialize(moq, IMQUIC_MOQ_REQUEST_UPDATE, parameters, &bytes[offset], blen-offset, ¶ms_num); IMQUIC_MOQ_ADD_MESSAGE_LENGTH(); #ifdef HAVE_QLOG if(moq->conn->qlog != NULL && moq->conn->qlog->moq) { json_t *message = imquic_qlog_moq_message_prepare("request_update"); json_object_set_new(message, "request_id", json_integer(request_id)); - if(moq->version >= IMQUIC_MOQ_VERSION_14) + if(moq->version <= IMQUIC_MOQ_VERSION_16) { json_object_set_new(message, "subscription_request_id", json_integer(sub_request_id)); - if(moq->version <= IMQUIC_MOQ_VERSION_14) { - json_object_set_new(message, "start_group", json_integer(parameters->subscription_filter.start_location.group)); - json_object_set_new(message, "start_object", json_integer(parameters->subscription_filter.start_location.object)); - json_object_set_new(message, "end_group", json_integer(parameters->subscription_filter.end_group)); - json_object_set_new(message, "subscriber_priority", json_integer(parameters->subscriber_priority_set ? - parameters->subscriber_priority : 128)); - json_object_set_new(message, "forward", json_integer(parameters->forward)); + } else { + json_object_set_new(message, "required_request_id_delta", json_integer(sub_request_id)); } imquic_qlog_moq_message_add_request_parameters(message, moq->version, parameters, "parameters"); - imquic_moq_qlog_control_message_created(moq->conn->qlog, moq->control_stream_id, bytes, offset, message); + imquic_moq_qlog_control_message_created(moq->conn->qlog, + (moq_stream ? moq_stream->stream_id : moq->control_stream_id), bytes, offset, message); } #endif return offset; } -size_t imquic_moq_add_subscribe_ok(imquic_moq_context *moq, uint8_t *bytes, size_t blen, uint64_t request_id, - uint64_t track_alias, imquic_moq_request_parameters *parameters, GList *track_extensions) { - if(bytes == NULL || blen < 1) { +size_t imquic_moq_add_subscribe_ok(imquic_moq_context *moq, imquic_moq_stream *moq_stream, + uint8_t *bytes, size_t blen, uint64_t request_id, + uint64_t track_alias, imquic_moq_request_parameters *parameters, GList *track_properties) { + if(bytes == NULL || blen < 3 || (moq->version >= IMQUIC_MOQ_VERSION_17 && moq_stream == NULL)) { IMQUIC_LOG(IMQUIC_LOG_ERR, "[%s][MoQ] Can't add MoQ %s: invalid arguments\n", imquic_get_connection_name(moq->conn), imquic_moq_message_type_str(IMQUIC_MOQ_SUBSCRIBE_OK, moq->version)); return 0; } size_t offset = 0, len_offset = 0; IMQUIC_MOQ_ADD_MESSAGE_TYPE(IMQUIC_MOQ_SUBSCRIBE_OK); - offset += imquic_write_varint(request_id, &bytes[offset], blen-offset); - if(moq->version >= IMQUIC_MOQ_VERSION_12) - offset += imquic_write_varint(track_alias, &bytes[offset], blen-offset); - /* For versions older than v15, we need to add some attributes manually, - * but we'll read them from the parameters object the application passed */ - if(moq->version <= IMQUIC_MOQ_VERSION_14) { - offset += imquic_write_varint(parameters->expires, &bytes[offset], blen-offset); - bytes[offset] = parameters->group_order; - offset++; - bytes[offset] = parameters->largest_object_set; - offset++; - if(parameters->largest_object_set) { - offset += imquic_write_varint(parameters->largest_object.group, &bytes[offset], blen-offset); - offset += imquic_write_varint(parameters->largest_object.object, &bytes[offset], blen-offset); - } - } + if(moq->version <= IMQUIC_MOQ_VERSION_16) + offset += imquic_write_moqint(moq->version, request_id, &bytes[offset], blen-offset); + offset += imquic_write_moqint(moq->version, track_alias, &bytes[offset], blen-offset); uint8_t params_num = 0; - offset += imquic_moq_request_parameters_serialize(moq, parameters, &bytes[offset], blen-offset, ¶ms_num); - /* Check if there are extensions to encode */ - if(moq->version >= IMQUIC_MOQ_VERSION_16) { - uint8_t extensions[256]; - size_t extensions_len = 0; - extensions_len = imquic_moq_build_object_extensions(moq->version, track_extensions, extensions, sizeof(extensions)); - offset += imquic_moq_add_object_extensions(moq, &bytes[offset], blen-offset, extensions, extensions_len); - } + offset += imquic_moq_request_parameters_serialize(moq, IMQUIC_MOQ_SUBSCRIBE_OK, parameters, &bytes[offset], blen-offset, ¶ms_num); + /* Check if there are properties to encode */ + uint8_t properties[256]; + size_t properties_len = 0; + properties_len = imquic_moq_build_properties(moq->version, track_properties, properties, sizeof(properties)); + offset += imquic_moq_add_properties(moq, &bytes[offset], blen-offset, properties, properties_len); IMQUIC_MOQ_ADD_MESSAGE_LENGTH(); #ifdef HAVE_QLOG if(moq->conn->qlog != NULL && moq->conn->qlog->moq) { json_t *message = imquic_qlog_moq_message_prepare("subscribe_ok"); - json_object_set_new(message, "request_id", json_integer(request_id)); - if(moq->version >= IMQUIC_MOQ_VERSION_12) - json_object_set_new(message, "track_alias", json_integer(track_alias)); - if(moq->version <= IMQUIC_MOQ_VERSION_14) { - json_object_set_new(message, "expires", json_integer(parameters->expires)); - json_object_set_new(message, "group_order", json_integer(parameters->group_order)); - json_object_set_new(message, "content_exists", json_integer(parameters->largest_object_set)); - if(parameters->largest_object_set) { - json_object_set_new(message, "largest_group_id", json_integer(parameters->largest_object.group)); - json_object_set_new(message, "largest_object_id", json_integer(parameters->largest_object.object)); - } - } + if(moq->version <= IMQUIC_MOQ_VERSION_16) + json_object_set_new(message, "request_id", json_integer(request_id)); + json_object_set_new(message, "track_alias", json_integer(track_alias)); json_object_set_new(message, "number_of_parameters", json_integer(params_num)); imquic_qlog_moq_message_add_request_parameters(message, moq->version, parameters, "parameters"); - if(moq->version >= IMQUIC_MOQ_VERSION_16) - imquic_qlog_moq_message_add_extensions(message, track_extensions, "track_extensions"); - imquic_moq_qlog_control_message_created(moq->conn->qlog, moq->control_stream_id, bytes, offset, message); - } -#endif - return offset; -} - -size_t imquic_moq_add_subscribe_error(imquic_moq_context *moq, uint8_t *bytes, size_t blen, - uint64_t request_id, imquic_moq_request_error_code error, const char *reason, uint64_t track_alias) { - if(bytes == NULL || blen < 1 || (reason && strlen(reason) > 1024)) { - IMQUIC_LOG(IMQUIC_LOG_ERR, "[%s][MoQ] Can't add MoQ %s: invalid arguments\n", - imquic_get_connection_name(moq->conn), imquic_moq_message_type_str(IMQUIC_MOQ_SUBSCRIBE_ERROR, moq->version)); - return 0; - } - if(moq->version < IMQUIC_MOQ_VERSION_15) - error = (imquic_moq_request_error_code)imquic_moq_request_error_code_to_legacy(moq->version, error); - size_t offset = 0, len_offset = 0; - IMQUIC_MOQ_ADD_MESSAGE_TYPE(IMQUIC_MOQ_SUBSCRIBE_ERROR); - offset += imquic_write_varint(request_id, &bytes[offset], blen-offset); - offset += imquic_write_varint(error, &bytes[offset], blen-offset); - size_t reason_len = reason ? strlen(reason) : 0; - offset += imquic_write_varint(reason_len, &bytes[offset], blen-offset); - if(reason_len > 0) { - memcpy(&bytes[offset], reason, reason_len); - offset += reason_len; - } - if(moq->version < IMQUIC_MOQ_VERSION_12) - offset += imquic_write_varint(track_alias, &bytes[offset], blen-offset); - IMQUIC_MOQ_ADD_MESSAGE_LENGTH(); -#ifdef HAVE_QLOG - if(moq->conn->qlog != NULL && moq->conn->qlog->moq) { - json_t *message = imquic_qlog_moq_message_prepare(moq->version >= IMQUIC_MOQ_VERSION_15 ? "request_error" : "subscribe_error"); - json_object_set_new(message, "request_id", json_integer(request_id)); - if(moq->version < IMQUIC_MOQ_VERSION_12) - json_object_set_new(message, "track_alias", json_integer(track_alias)); - json_object_set_new(message, "error_code", json_integer(error)); - if(reason != NULL) - json_object_set_new(message, "reason", json_string(reason)); - imquic_moq_qlog_control_message_created(moq->conn->qlog, moq->control_stream_id, bytes, offset, message); + imquic_qlog_moq_message_add_properties(message, track_properties, "track_properties"); + imquic_moq_qlog_control_message_created(moq->conn->qlog, + (moq_stream ? moq_stream->stream_id : moq->control_stream_id), bytes, offset, message); } #endif return offset; } size_t imquic_moq_add_unsubscribe(imquic_moq_context *moq, uint8_t *bytes, size_t blen, uint64_t request_id) { - if(bytes == NULL || blen < 1) { + if(bytes == NULL || blen < 2) { IMQUIC_LOG(IMQUIC_LOG_ERR, "[%s][MoQ] Can't add MoQ %s: invalid arguments\n", imquic_get_connection_name(moq->conn), imquic_moq_message_type_str(IMQUIC_MOQ_UNSUBSCRIBE, moq->version)); return 0; } size_t offset = 0, len_offset = 0; IMQUIC_MOQ_ADD_MESSAGE_TYPE(IMQUIC_MOQ_UNSUBSCRIBE); - offset += imquic_write_varint(request_id, &bytes[offset], blen-offset); + offset += imquic_write_moqint(moq->version, request_id, &bytes[offset], blen-offset); IMQUIC_MOQ_ADD_MESSAGE_LENGTH(); #ifdef HAVE_QLOG if(moq->conn->qlog != NULL && moq->conn->qlog->moq) { @@ -5579,20 +4588,23 @@ size_t imquic_moq_add_unsubscribe(imquic_moq_context *moq, uint8_t *bytes, size_ return offset; } -size_t imquic_moq_add_publish_done(imquic_moq_context *moq, uint8_t *bytes, size_t blen, uint64_t request_id, +size_t imquic_moq_add_publish_done(imquic_moq_context *moq, imquic_moq_stream *moq_stream, + uint8_t *bytes, size_t blen, uint64_t request_id, imquic_moq_pub_done_code status, uint64_t streams_count, const char *reason) { - if(bytes == NULL || blen < 1 || (reason && strlen(reason) > 1024)) { + if(bytes == NULL || blen < 5 || (reason && strlen(reason) > 1024) || + (moq->version >= IMQUIC_MOQ_VERSION_17 && moq_stream == NULL)) { IMQUIC_LOG(IMQUIC_LOG_ERR, "[%s][MoQ] Can't add MoQ %s: invalid arguments\n", imquic_get_connection_name(moq->conn), imquic_moq_message_type_str(IMQUIC_MOQ_PUBLISH_DONE, moq->version)); return 0; } size_t offset = 0, len_offset = 0; IMQUIC_MOQ_ADD_MESSAGE_TYPE(IMQUIC_MOQ_PUBLISH_DONE); - offset += imquic_write_varint(request_id, &bytes[offset], blen-offset); - offset += imquic_write_varint(status, &bytes[offset], blen-offset); - offset += imquic_write_varint(streams_count, &bytes[offset], blen-offset); + if(moq->version <= IMQUIC_MOQ_VERSION_16) + offset += imquic_write_moqint(moq->version, request_id, &bytes[offset], blen-offset); + offset += imquic_write_moqint(moq->version, status, &bytes[offset], blen-offset); + offset += imquic_write_moqint(moq->version, streams_count, &bytes[offset], blen-offset); size_t reason_len = reason ? strlen(reason) : 0; - offset += imquic_write_varint(reason_len, &bytes[offset], blen-offset); + offset += imquic_write_moqint(moq->version, reason_len, &bytes[offset], blen-offset); if(reason_len > 0) { memcpy(&bytes[offset], reason, reason_len); offset += reason_len; @@ -5601,184 +4613,132 @@ size_t imquic_moq_add_publish_done(imquic_moq_context *moq, uint8_t *bytes, size #ifdef HAVE_QLOG if(moq->conn->qlog != NULL && moq->conn->qlog->moq) { json_t *message = imquic_qlog_moq_message_prepare("publish_done"); - json_object_set_new(message, "request_id", json_integer(request_id)); + if(moq->version <= IMQUIC_MOQ_VERSION_16) + json_object_set_new(message, "request_id", json_integer(request_id)); json_object_set_new(message, "status_code", json_integer(status)); json_object_set_new(message, "streams_count", json_integer(streams_count)); if(reason != NULL) json_object_set_new(message, "reason", json_string(reason)); - imquic_moq_qlog_control_message_created(moq->conn->qlog, moq->control_stream_id, bytes, offset, message); + imquic_moq_qlog_control_message_created(moq->conn->qlog, + (moq_stream ? moq_stream->stream_id : moq->control_stream_id), bytes, offset, message); } #endif return offset; } size_t imquic_moq_add_subscribe_namespace(imquic_moq_context *moq, imquic_moq_stream *moq_stream, - uint8_t *bytes, size_t blen, uint64_t request_id, imquic_moq_namespace *track_namespace, + uint8_t *bytes, size_t blen, uint64_t request_id, uint64_t required_id_delta, imquic_moq_namespace *track_namespace, imquic_moq_subscribe_namespace_options subscribe_options, imquic_moq_request_parameters *parameters) { - if(bytes == NULL || blen < 1 || track_namespace == NULL || - (moq->version >= IMQUIC_MOQ_VERSION_16 && moq_stream == NULL)) { + if(bytes == NULL || blen < 1 || moq_stream == NULL) { IMQUIC_LOG(IMQUIC_LOG_ERR, "[%s][MoQ] Can't add MoQ %s: invalid arguments\n", imquic_get_connection_name(moq->conn), imquic_moq_message_type_str(IMQUIC_MOQ_SUBSCRIBE_NAMESPACE, moq->version)); return 0; } size_t offset = 0, len_offset = 0; IMQUIC_MOQ_ADD_MESSAGE_TYPE(IMQUIC_MOQ_SUBSCRIBE_NAMESPACE); - offset += imquic_write_varint(request_id, &bytes[offset], blen-offset); + offset += imquic_write_moqint(moq->version, request_id, &bytes[offset], blen-offset); + if(moq->version >= IMQUIC_MOQ_VERSION_17) + offset += imquic_write_moqint(moq->version, required_id_delta, &bytes[offset], blen-offset); IMQUIC_MOQ_ADD_NAMESPACES(IMQUIC_MOQ_SUBSCRIBE_NAMESPACE); - if(moq->version >= IMQUIC_MOQ_VERSION_16) - offset += imquic_write_varint(subscribe_options, &bytes[offset], blen-offset); + offset += imquic_write_moqint(moq->version, subscribe_options, &bytes[offset], blen-offset); uint8_t params_num = 0; - offset += imquic_moq_request_parameters_serialize(moq, parameters, &bytes[offset], blen-offset, ¶ms_num); + offset += imquic_moq_request_parameters_serialize(moq, IMQUIC_MOQ_SUBSCRIBE_NAMESPACE, parameters, &bytes[offset], blen-offset, ¶ms_num); IMQUIC_MOQ_ADD_MESSAGE_LENGTH(); #ifdef HAVE_QLOG if(moq->conn->qlog != NULL && moq->conn->qlog->moq) { - if(moq->version >= IMQUIC_MOQ_VERSION_16) - imquic_moq_qlog_stream_type_set(moq->conn->qlog, FALSE, moq_stream->stream_id, "subscribe_namespace"); + imquic_moq_qlog_stream_type_set(moq->conn->qlog, TRUE, moq_stream->stream_id, "subscribe_namespace"); json_t *message = imquic_qlog_moq_message_prepare("subscribe_namespace"); json_object_set_new(message, "request_id", json_integer(request_id)); + if(moq->version >= IMQUIC_MOQ_VERSION_17) + json_object_set_new(message, "required_request_id_delta", json_integer(required_id_delta)); imquic_qlog_moq_message_add_namespace(message, track_namespace, "track_namespace_prefix"); - if(moq->version >= IMQUIC_MOQ_VERSION_16) - json_object_set_new(message, "subscribe_options", json_integer(subscribe_options)); + json_object_set_new(message, "subscribe_options", json_integer(subscribe_options)); json_object_set_new(message, "number_of_parameters", json_integer(params_num)); imquic_qlog_moq_message_add_request_parameters(message, moq->version, parameters, "parameters"); - imquic_moq_qlog_control_message_created(moq->conn->qlog, - (moq_stream ? moq_stream->stream_id : moq->control_stream_id), bytes, offset, message); - } -#endif - return offset; -} - -size_t imquic_moq_add_namespace(imquic_moq_context *moq, imquic_moq_stream *moq_stream, - uint8_t *bytes, size_t blen, imquic_moq_namespace *track_namespace) { - if(bytes == NULL || blen < 1) { - IMQUIC_LOG(IMQUIC_LOG_ERR, "[%s][MoQ] Can't add MoQ %s: invalid arguments\n", - imquic_get_connection_name(moq->conn), imquic_moq_message_type_str(IMQUIC_MOQ_NAMESPACE, moq->version)); - return 0; - } - size_t offset = 0, len_offset = 0; - /* FIXME A tuple of size 0 is allowed here, this macro needs fixing */ - IMQUIC_MOQ_ADD_MESSAGE_TYPE(IMQUIC_MOQ_NAMESPACE); - /* FIXME A tuple of size 0 is allowed here, this macro needs fixing */ - IMQUIC_MOQ_ADD_NAMESPACES(IMQUIC_MOQ_NAMESPACE); - IMQUIC_MOQ_ADD_MESSAGE_LENGTH(); -#ifdef HAVE_QLOG - if(moq->conn->qlog != NULL && moq->conn->qlog->moq) { - json_t *message = imquic_qlog_moq_message_prepare("namespace"); - imquic_qlog_moq_message_add_namespace(message, track_namespace, "track_namespace_suffix"); - imquic_moq_qlog_control_message_created(moq->conn->qlog, moq_stream->stream_id, bytes, offset, message); - } -#endif - return offset; -} - -size_t imquic_moq_add_namespace_done(imquic_moq_context *moq, imquic_moq_stream *moq_stream, - uint8_t *bytes, size_t blen, imquic_moq_namespace *track_namespace) { - if(bytes == NULL || blen < 1) { - IMQUIC_LOG(IMQUIC_LOG_ERR, "[%s][MoQ] Can't add MoQ %s: invalid arguments\n", - imquic_get_connection_name(moq->conn), imquic_moq_message_type_str(IMQUIC_MOQ_NAMESPACE_DONE, moq->version)); - return 0; - } - size_t offset = 0, len_offset = 0; - /* FIXME A tuple of size 0 is allowed here, this macro needs fixing */ - IMQUIC_MOQ_ADD_MESSAGE_TYPE(IMQUIC_MOQ_NAMESPACE_DONE); - /* FIXME A tuple of size 0 is allowed here, this macro needs fixing */ - IMQUIC_MOQ_ADD_NAMESPACES(IMQUIC_MOQ_NAMESPACE_DONE); - IMQUIC_MOQ_ADD_MESSAGE_LENGTH(); -#ifdef HAVE_QLOG - if(moq->conn->qlog != NULL && moq->conn->qlog->moq) { - json_t *message = imquic_qlog_moq_message_prepare("namespace"); - imquic_qlog_moq_message_add_namespace(message, track_namespace, "track_namespace_suffix"); imquic_moq_qlog_control_message_created(moq->conn->qlog, moq_stream->stream_id, bytes, offset, message); } #endif return offset; } -size_t imquic_moq_add_subscribe_namespace_ok(imquic_moq_context *moq, uint8_t *bytes, size_t blen, uint64_t request_id) { - if(bytes == NULL || blen < 1) { +size_t imquic_moq_add_namespace(imquic_moq_context *moq, imquic_moq_stream *moq_stream, + uint8_t *bytes, size_t blen, imquic_moq_namespace *track_namespace) { + if(bytes == NULL || blen < 1 || moq_stream == NULL) { IMQUIC_LOG(IMQUIC_LOG_ERR, "[%s][MoQ] Can't add MoQ %s: invalid arguments\n", - imquic_get_connection_name(moq->conn), imquic_moq_message_type_str(IMQUIC_MOQ_SUBSCRIBE_NAMESPACE_OK, moq->version)); + imquic_get_connection_name(moq->conn), imquic_moq_message_type_str(IMQUIC_MOQ_NAMESPACE, moq->version)); return 0; } size_t offset = 0, len_offset = 0; - IMQUIC_MOQ_ADD_MESSAGE_TYPE(IMQUIC_MOQ_SUBSCRIBE_NAMESPACE_OK); - offset += imquic_write_varint(request_id, &bytes[offset], blen-offset); + /* FIXME A tuple of size 0 is allowed here, this macro needs fixing */ + IMQUIC_MOQ_ADD_MESSAGE_TYPE(IMQUIC_MOQ_NAMESPACE); + /* FIXME A tuple of size 0 is allowed here, this macro needs fixing */ + IMQUIC_MOQ_ADD_NAMESPACES(IMQUIC_MOQ_NAMESPACE); IMQUIC_MOQ_ADD_MESSAGE_LENGTH(); #ifdef HAVE_QLOG if(moq->conn->qlog != NULL && moq->conn->qlog->moq) { - json_t *message = imquic_qlog_moq_message_prepare(moq->version >= IMQUIC_MOQ_VERSION_15 ? "request_ok" : "subscribe_namespace_ok"); - json_object_set_new(message, "request_id", json_integer(request_id)); - imquic_moq_qlog_control_message_created(moq->conn->qlog, moq->control_stream_id, bytes, offset, message); + json_t *message = imquic_qlog_moq_message_prepare("namespace"); + imquic_qlog_moq_message_add_namespace(message, track_namespace, "track_namespace_suffix"); + imquic_moq_qlog_control_message_created(moq->conn->qlog, moq_stream->stream_id, bytes, offset, message); } #endif return offset; } -size_t imquic_moq_add_subscribe_namespace_error(imquic_moq_context *moq, uint8_t *bytes, size_t blen, - uint64_t request_id, imquic_moq_request_error_code error, const char *reason) { - if(bytes == NULL || blen < 1 || (reason && strlen(reason) > 1024)) { +size_t imquic_moq_add_namespace_done(imquic_moq_context *moq, imquic_moq_stream *moq_stream, + uint8_t *bytes, size_t blen, imquic_moq_namespace *track_namespace) { + if(bytes == NULL || blen < 1 || moq_stream == NULL) { IMQUIC_LOG(IMQUIC_LOG_ERR, "[%s][MoQ] Can't add MoQ %s: invalid arguments\n", - imquic_get_connection_name(moq->conn), imquic_moq_message_type_str(IMQUIC_MOQ_SUBSCRIBE_NAMESPACE_ERROR, moq->version)); + imquic_get_connection_name(moq->conn), imquic_moq_message_type_str(IMQUIC_MOQ_NAMESPACE_DONE, moq->version)); return 0; } - if(moq->version < IMQUIC_MOQ_VERSION_15) - error = (imquic_moq_request_error_code)imquic_moq_request_error_code_to_legacy(moq->version, error); size_t offset = 0, len_offset = 0; - IMQUIC_MOQ_ADD_MESSAGE_TYPE(IMQUIC_MOQ_SUBSCRIBE_NAMESPACE_ERROR); - offset += imquic_write_varint(request_id, &bytes[offset], blen-offset); - offset += imquic_write_varint(error, &bytes[offset], blen-offset); - size_t reason_len = reason ? strlen(reason) : 0; - offset += imquic_write_varint(reason_len, &bytes[offset], blen-offset); - if(reason_len > 0) { - memcpy(&bytes[offset], reason, reason_len); - offset += reason_len; - } + /* FIXME A tuple of size 0 is allowed here, this macro needs fixing */ + IMQUIC_MOQ_ADD_MESSAGE_TYPE(IMQUIC_MOQ_NAMESPACE_DONE); + /* FIXME A tuple of size 0 is allowed here, this macro needs fixing */ + IMQUIC_MOQ_ADD_NAMESPACES(IMQUIC_MOQ_NAMESPACE_DONE); IMQUIC_MOQ_ADD_MESSAGE_LENGTH(); #ifdef HAVE_QLOG if(moq->conn->qlog != NULL && moq->conn->qlog->moq) { - json_t *message = imquic_qlog_moq_message_prepare(moq->version >= IMQUIC_MOQ_VERSION_15 ? "request_error" : "subscribe_namespace_error"); - json_object_set_new(message, "request_id", json_integer(request_id)); - json_object_set_new(message, "error_code", json_integer(error)); - if(reason != NULL) - json_object_set_new(message, "reason", json_string(reason)); - imquic_moq_qlog_control_message_created(moq->conn->qlog, moq->control_stream_id, bytes, offset, message); + json_t *message = imquic_qlog_moq_message_prepare("namespace_done"); + imquic_qlog_moq_message_add_namespace(message, track_namespace, "track_namespace_suffix"); + imquic_moq_qlog_control_message_created(moq->conn->qlog, moq_stream->stream_id, bytes, offset, message); } #endif return offset; } -size_t imquic_moq_add_unsubscribe_namespace(imquic_moq_context *moq, uint8_t *bytes, size_t blen, uint64_t request_id, imquic_moq_namespace *track_namespace) { - if(bytes == NULL || blen < 1 || (moq->version <= IMQUIC_MOQ_VERSION_14 && track_namespace == NULL)) { +size_t imquic_moq_add_publish_blocked(imquic_moq_context *moq, imquic_moq_stream *moq_stream, + uint8_t *bytes, size_t blen, imquic_moq_namespace *track_namespace, imquic_moq_name *track_name) { + if(bytes == NULL || blen < 1 || moq_stream == NULL) { IMQUIC_LOG(IMQUIC_LOG_ERR, "[%s][MoQ] Can't add MoQ %s: invalid arguments\n", - imquic_get_connection_name(moq->conn), imquic_moq_message_type_str(IMQUIC_MOQ_UNSUBSCRIBE_NAMESPACE, moq->version)); + imquic_get_connection_name(moq->conn), imquic_moq_message_type_str(IMQUIC_MOQ_NAMESPACE_DONE, moq->version)); return 0; } size_t offset = 0, len_offset = 0; - IMQUIC_MOQ_ADD_MESSAGE_TYPE(IMQUIC_MOQ_UNSUBSCRIBE_NAMESPACE); - if(moq->version >= IMQUIC_MOQ_VERSION_15) - offset += imquic_write_varint(request_id, &bytes[offset], blen-offset); - if(moq->version <= IMQUIC_MOQ_VERSION_14) { - IMQUIC_MOQ_ADD_NAMESPACES(IMQUIC_MOQ_UNSUBSCRIBE_NAMESPACE); - } + /* FIXME A tuple of size 0 is allowed here, this macro needs fixing */ + IMQUIC_MOQ_ADD_MESSAGE_TYPE(IMQUIC_MOQ_PUBLISH_BLOCKED); + /* FIXME A tuple of size 0 is allowed here, this macro needs fixing */ + IMQUIC_MOQ_ADD_NAMESPACES(IMQUIC_MOQ_PUBLISH_BLOCKED); + IMQUIC_MOQ_ADD_TRACKNAME(IMQUIC_MOQ_PUBLISH_BLOCKED); IMQUIC_MOQ_ADD_MESSAGE_LENGTH(); #ifdef HAVE_QLOG if(moq->conn->qlog != NULL && moq->conn->qlog->moq) { - json_t *message = imquic_qlog_moq_message_prepare("unsubscribe_namespace"); - if(moq->version >= IMQUIC_MOQ_VERSION_15) - json_object_set_new(message, "request_id", json_integer(request_id)); - if(moq->version <= IMQUIC_MOQ_VERSION_14) - imquic_qlog_moq_message_add_namespace(message, track_namespace, "track_namespace"); - imquic_moq_qlog_control_message_created(moq->conn->qlog, moq->control_stream_id, bytes, offset, message); + json_t *message = imquic_qlog_moq_message_prepare("publish_blocked"); + imquic_qlog_moq_message_add_namespace(message, track_namespace, "track_namespace_suffix"); + imquic_qlog_moq_message_add_track(message, track_name); + imquic_moq_qlog_control_message_created(moq->conn->qlog, moq_stream->stream_id, bytes, offset, message); } #endif return offset; } -size_t imquic_moq_add_fetch(imquic_moq_context *moq, uint8_t *bytes, size_t blen, imquic_moq_fetch_type type, - uint64_t request_id, uint64_t joining_request_id, uint64_t preceding_group_offset, +size_t imquic_moq_add_fetch(imquic_moq_context *moq, imquic_moq_stream *moq_stream, + uint8_t *bytes, size_t blen, imquic_moq_fetch_type type, + uint64_t request_id, uint64_t required_id_delta, uint64_t joining_request_id, uint64_t preceding_group_offset, imquic_moq_namespace *track_namespace, imquic_moq_name *track_name, imquic_moq_location_range *range, imquic_moq_request_parameters *parameters) { - if(bytes == NULL || blen < 1 || (range == NULL && type == IMQUIC_MOQ_FETCH_STANDALONE)) { + if(bytes == NULL || blen < 1 || (range == NULL && type == IMQUIC_MOQ_FETCH_STANDALONE) || + (moq->version >= IMQUIC_MOQ_VERSION_17 && moq_stream == NULL)) { IMQUIC_LOG(IMQUIC_LOG_ERR, "[%s][MoQ] Can't add MoQ %s: invalid arguments\n", imquic_get_connection_name(moq->conn), imquic_moq_message_type_str(IMQUIC_MOQ_FETCH, moq->version)); return 0; @@ -5796,40 +4756,32 @@ size_t imquic_moq_add_fetch(imquic_moq_context *moq, uint8_t *bytes, size_t blen } size_t offset = 0, len_offset = 0; IMQUIC_MOQ_ADD_MESSAGE_TYPE(IMQUIC_MOQ_FETCH); - offset += imquic_write_varint(request_id, &bytes[offset], blen-offset); - /* For versions older than v15, we need to add some attributes manually, - * but we'll read them from the parameters object the application passed */ - if(moq->version <= IMQUIC_MOQ_VERSION_14) { - bytes[offset] = parameters->subscriber_priority_set ? - parameters->subscriber_priority : 128; - offset++; - bytes[offset] = parameters->group_order; - offset++; - } - offset += imquic_write_varint(type, &bytes[offset], blen-offset); + offset += imquic_write_moqint(moq->version, request_id, &bytes[offset], blen-offset); + if(moq->version >= IMQUIC_MOQ_VERSION_17) + offset += imquic_write_moqint(moq->version, required_id_delta, &bytes[offset], blen-offset); + offset += imquic_write_moqint(moq->version, type, &bytes[offset], blen-offset); if(type == IMQUIC_MOQ_FETCH_STANDALONE) { IMQUIC_MOQ_ADD_NAMESPACES(IMQUIC_MOQ_FETCH); IMQUIC_MOQ_ADD_TRACKNAME(IMQUIC_MOQ_FETCH); - offset += imquic_write_varint(range->start.group, &bytes[offset], blen-offset); - offset += imquic_write_varint(range->start.object, &bytes[offset], blen-offset); - offset += imquic_write_varint(range->end.group, &bytes[offset], blen-offset); - offset += imquic_write_varint(range->end.object, &bytes[offset], blen-offset); + offset += imquic_write_moqint(moq->version, range->start.group, &bytes[offset], blen-offset); + offset += imquic_write_moqint(moq->version, range->start.object, &bytes[offset], blen-offset); + offset += imquic_write_moqint(moq->version, range->end.group, &bytes[offset], blen-offset); + offset += imquic_write_moqint(moq->version, range->end.object, &bytes[offset], blen-offset); } else { - offset += imquic_write_varint(joining_request_id, &bytes[offset], blen-offset); - offset += imquic_write_varint(preceding_group_offset, &bytes[offset], blen-offset); + offset += imquic_write_moqint(moq->version, joining_request_id, &bytes[offset], blen-offset); + offset += imquic_write_moqint(moq->version, preceding_group_offset, &bytes[offset], blen-offset); } uint8_t params_num = 0; - offset += imquic_moq_request_parameters_serialize(moq, parameters, &bytes[offset], blen-offset, ¶ms_num); + offset += imquic_moq_request_parameters_serialize(moq, IMQUIC_MOQ_FETCH, parameters, &bytes[offset], blen-offset, ¶ms_num); IMQUIC_MOQ_ADD_MESSAGE_LENGTH(); #ifdef HAVE_QLOG if(moq->conn->qlog != NULL && moq->conn->qlog->moq) { + if(moq_stream != NULL) + imquic_moq_qlog_stream_type_set(moq->conn->qlog, TRUE, moq_stream->stream_id, "fetch"); json_t *message = imquic_qlog_moq_message_prepare("fetch"); json_object_set_new(message, "request_id", json_integer(request_id)); - if(moq->version <= IMQUIC_MOQ_VERSION_14) { - json_object_set_new(message, "subscriber_priority", json_integer(parameters->subscriber_priority_set ? - parameters->subscriber_priority : 128)); - json_object_set_new(message, "group_order", json_integer(parameters->group_order)); - } + if(moq->version >= IMQUIC_MOQ_VERSION_17) + json_object_set_new(message, "required_request_id_delta", json_integer(required_id_delta)); if(type == IMQUIC_MOQ_FETCH_STANDALONE) { imquic_qlog_moq_message_add_namespace(message, track_namespace, "track_namespace"); imquic_qlog_moq_message_add_track(message, track_name); @@ -5843,21 +4795,22 @@ size_t imquic_moq_add_fetch(imquic_moq_context *moq, uint8_t *bytes, size_t blen } json_object_set_new(message, "number_of_parameters", json_integer(params_num)); imquic_qlog_moq_message_add_request_parameters(message, moq->version, parameters, "parameters"); - imquic_moq_qlog_control_message_created(moq->conn->qlog, moq->control_stream_id, bytes, offset, message); + imquic_moq_qlog_control_message_created(moq->conn->qlog, + (moq_stream ? moq_stream->stream_id : moq->control_stream_id), bytes, offset, message); } #endif return offset; } size_t imquic_moq_add_fetch_cancel(imquic_moq_context *moq, uint8_t *bytes, size_t blen, uint64_t request_id) { - if(bytes == NULL || blen < 1) { + if(bytes == NULL || blen < 1 || moq->version >= IMQUIC_MOQ_VERSION_17) { IMQUIC_LOG(IMQUIC_LOG_ERR, "[%s][MoQ] Can't add MoQ %s: invalid arguments\n", imquic_get_connection_name(moq->conn), imquic_moq_message_type_str(IMQUIC_MOQ_FETCH_CANCEL, moq->version)); return 0; } size_t offset = 0, len_offset = 0; IMQUIC_MOQ_ADD_MESSAGE_TYPE(IMQUIC_MOQ_FETCH_CANCEL); - offset += imquic_write_varint(request_id, &bytes[offset], blen-offset); + offset += imquic_write_moqint(moq->version, request_id, &bytes[offset], blen-offset); IMQUIC_MOQ_ADD_MESSAGE_LENGTH(); #ifdef HAVE_QLOG if(moq->conn->qlog != NULL && moq->conn->qlog->moq) { @@ -5869,234 +4822,83 @@ size_t imquic_moq_add_fetch_cancel(imquic_moq_context *moq, uint8_t *bytes, size return offset; } -size_t imquic_moq_add_fetch_ok(imquic_moq_context *moq, uint8_t *bytes, size_t blen, uint64_t request_id, - uint8_t end_of_track, imquic_moq_location *end_location, imquic_moq_request_parameters *parameters, GList *track_extensions) { - if(bytes == NULL || blen < 1) { +size_t imquic_moq_add_fetch_ok(imquic_moq_context *moq, imquic_moq_stream *moq_stream, + uint8_t *bytes, size_t blen, uint64_t request_id, + uint8_t end_of_track, imquic_moq_location *end_location, imquic_moq_request_parameters *parameters, GList *track_properties) { + if(bytes == NULL || blen < 1 || (moq->version >= IMQUIC_MOQ_VERSION_17 && moq_stream == NULL)) { IMQUIC_LOG(IMQUIC_LOG_ERR, "[%s][MoQ] Can't add MoQ %s: invalid arguments\n", imquic_get_connection_name(moq->conn), imquic_moq_message_type_str(IMQUIC_MOQ_FETCH_OK, moq->version)); return 0; } size_t offset = 0, len_offset = 0; IMQUIC_MOQ_ADD_MESSAGE_TYPE(IMQUIC_MOQ_FETCH_OK); - offset += imquic_write_varint(request_id, &bytes[offset], blen-offset); - /* For versions older than v15, we need to add some attributes manually, - * but we'll read them from the parameters object the application passed */ - if(moq->version <= IMQUIC_MOQ_VERSION_14) { - bytes[offset] = parameters->group_order; - offset++; - } + if(moq->version <= IMQUIC_MOQ_VERSION_16) + offset += imquic_write_moqint(moq->version, request_id, &bytes[offset], blen-offset); bytes[offset] = end_of_track; offset++; - offset += imquic_write_varint(end_location->group, &bytes[offset], blen-offset); - offset += imquic_write_varint(end_location->object, &bytes[offset], blen-offset); + offset += imquic_write_moqint(moq->version, end_location->group, &bytes[offset], blen-offset); + offset += imquic_write_moqint(moq->version, end_location->object, &bytes[offset], blen-offset); uint8_t params_num = 0; - offset += imquic_moq_request_parameters_serialize(moq, parameters, &bytes[offset], blen-offset, ¶ms_num); - /* Check if there are extensions to encode */ - if(moq->version >= IMQUIC_MOQ_VERSION_16) { - uint8_t extensions[256]; - size_t extensions_len = 0; - extensions_len = imquic_moq_build_object_extensions(moq->version, track_extensions, extensions, sizeof(extensions)); - offset += imquic_moq_add_object_extensions(moq, &bytes[offset], blen-offset, extensions, extensions_len); - } + offset += imquic_moq_request_parameters_serialize(moq, IMQUIC_MOQ_FETCH_OK, parameters, &bytes[offset], blen-offset, ¶ms_num); + /* Check if there are properties to encode */ + uint8_t properties[256]; + size_t properties_len = 0; + properties_len = imquic_moq_build_properties(moq->version, track_properties, properties, sizeof(properties)); + offset += imquic_moq_add_properties(moq, &bytes[offset], blen-offset, properties, properties_len); IMQUIC_MOQ_ADD_MESSAGE_LENGTH(); #ifdef HAVE_QLOG if(moq->conn->qlog != NULL && moq->conn->qlog->moq) { json_t *message = imquic_qlog_moq_message_prepare("fetch_ok"); - json_object_set_new(message, "request_id", json_integer(request_id)); - if(moq->version <= IMQUIC_MOQ_VERSION_14) { - json_object_set_new(message, "group_order", json_integer(parameters->group_order)); - } + if(moq->version <= IMQUIC_MOQ_VERSION_16) + json_object_set_new(message, "request_id", json_integer(request_id)); json_object_set_new(message, "end_of_track", json_integer(end_of_track)); json_object_set_new(message, "largest_group_id", json_integer(end_location->group)); json_object_set_new(message, "largest_object_id", json_integer(end_location->object)); json_object_set_new(message, "number_of_parameters", json_integer(params_num)); imquic_qlog_moq_message_add_request_parameters(message, moq->version, parameters, "parameters"); - if(moq->version >= IMQUIC_MOQ_VERSION_16) - imquic_qlog_moq_message_add_extensions(message, track_extensions, "track_extensions"); - imquic_moq_qlog_control_message_created(moq->conn->qlog, moq->control_stream_id, bytes, offset, message); - } -#endif - return offset; -} - -size_t imquic_moq_add_fetch_error(imquic_moq_context *moq, uint8_t *bytes, size_t blen, - uint64_t request_id, imquic_moq_request_error_code error, const char *reason) { - if(bytes == NULL || blen < 1 || (reason && strlen(reason) > 1024)) { - IMQUIC_LOG(IMQUIC_LOG_ERR, "[%s][MoQ] Can't add MoQ %s: invalid arguments\n", - imquic_get_connection_name(moq->conn), imquic_moq_message_type_str(IMQUIC_MOQ_FETCH_ERROR, moq->version)); - return 0; - } - if(moq->version < IMQUIC_MOQ_VERSION_15) - error = (imquic_moq_request_error_code)imquic_moq_request_error_code_to_legacy(moq->version, error); - size_t offset = 0, len_offset = 0; - IMQUIC_MOQ_ADD_MESSAGE_TYPE(IMQUIC_MOQ_FETCH_ERROR); - offset += imquic_write_varint(request_id, &bytes[offset], blen-offset); - offset += imquic_write_varint(error, &bytes[offset], blen-offset); - size_t reason_len = reason ? strlen(reason) : 0; - offset += imquic_write_varint(reason_len, &bytes[offset], blen-offset); - if(reason_len > 0) { - memcpy(&bytes[offset], reason, reason_len); - offset += reason_len; - } - IMQUIC_MOQ_ADD_MESSAGE_LENGTH(); -#ifdef HAVE_QLOG - if(moq->conn->qlog != NULL && moq->conn->qlog->moq) { - json_t *message = imquic_qlog_moq_message_prepare(moq->version >= IMQUIC_MOQ_VERSION_15 ? "request_error" : "fetch_error"); - json_object_set_new(message, "request_id", json_integer(request_id)); - json_object_set_new(message, "error_code", json_integer(error)); - if(reason != NULL) - json_object_set_new(message, "reason", json_string(reason)); - imquic_moq_qlog_control_message_created(moq->conn->qlog, moq->control_stream_id, bytes, offset, message); + imquic_qlog_moq_message_add_properties(message, track_properties, "track_properties"); + imquic_moq_qlog_control_message_created(moq->conn->qlog, + (moq_stream ? moq_stream->stream_id : moq->control_stream_id), bytes, offset, message); } #endif return offset; } -size_t imquic_moq_add_track_status(imquic_moq_context *moq, uint8_t *bytes, size_t blen, uint64_t request_id, +size_t imquic_moq_add_track_status(imquic_moq_context *moq, imquic_moq_stream *moq_stream, uint8_t *bytes, size_t blen, uint64_t request_id, imquic_moq_namespace *track_namespace, imquic_moq_name *track_name, imquic_moq_request_parameters *parameters) { if(bytes == NULL || blen < 1 || track_namespace == NULL || - track_name == NULL || (track_name->buffer == NULL && track_name->length > 0)) { + track_name == NULL || (track_name->buffer == NULL && track_name->length > 0) || + (moq->version >= IMQUIC_MOQ_VERSION_17 && moq_stream == NULL)) { IMQUIC_LOG(IMQUIC_LOG_ERR, "[%s][MoQ] Can't add MoQ %s: invalid arguments\n", imquic_get_connection_name(moq->conn), imquic_moq_message_type_str(IMQUIC_MOQ_TRACK_STATUS, moq->version)); return 0; } size_t offset = 0, len_offset = 0; IMQUIC_MOQ_ADD_MESSAGE_TYPE(IMQUIC_MOQ_TRACK_STATUS); - offset += imquic_write_varint(request_id, &bytes[offset], blen-offset); + offset += imquic_write_moqint(moq->version, request_id, &bytes[offset], blen-offset); IMQUIC_MOQ_ADD_NAMESPACES(IMQUIC_MOQ_TRACK_STATUS); IMQUIC_MOQ_ADD_TRACKNAME(IMQUIC_MOQ_TRACK_STATUS); - /* For versions older than v15, we need to add some attributes manually, - * but we'll read them from the parameters object the application passed */ - if(moq->version <= IMQUIC_MOQ_VERSION_14) { - bytes[offset] = parameters->subscriber_priority_set ? - parameters->subscriber_priority : 128; - offset++; - bytes[offset] = parameters->group_order; - offset++; - bytes[offset] = parameters->forward; - offset++; - offset += imquic_write_varint(parameters->subscription_filter.type, &bytes[offset], blen-offset); - if(parameters->subscription_filter.type == IMQUIC_MOQ_FILTER_ABSOLUTE_START || - parameters->subscription_filter.type == IMQUIC_MOQ_FILTER_ABSOLUTE_RANGE) { - offset += imquic_write_varint(parameters->subscription_filter.start_location.group, &bytes[offset], blen-offset); - offset += imquic_write_varint(parameters->subscription_filter.start_location.object, &bytes[offset], blen-offset); - } - if(parameters->subscription_filter.type == IMQUIC_MOQ_FILTER_ABSOLUTE_RANGE) - offset += imquic_write_varint(parameters->subscription_filter.end_group, &bytes[offset], blen-offset); - } uint8_t params_num = 0; - offset += imquic_moq_request_parameters_serialize(moq, parameters, &bytes[offset], blen-offset, ¶ms_num); + offset += imquic_moq_request_parameters_serialize(moq, IMQUIC_MOQ_TRACK_STATUS, parameters, &bytes[offset], blen-offset, ¶ms_num); IMQUIC_MOQ_ADD_MESSAGE_LENGTH(); #ifdef HAVE_QLOG if(moq->conn->qlog != NULL && moq->conn->qlog->moq) { + if(moq_stream != NULL) + imquic_moq_qlog_stream_type_set(moq->conn->qlog, TRUE, moq_stream->stream_id, "track_status"); json_t *message = imquic_qlog_moq_message_prepare("track_status"); json_object_set_new(message, "request_id", json_integer(request_id)); imquic_qlog_moq_message_add_namespace(message, track_namespace, "track_namespace"); imquic_qlog_moq_message_add_track(message, track_name); - if(moq->version <= IMQUIC_MOQ_VERSION_14) { - json_object_set_new(message, "subscriber_priority", json_integer(parameters->subscriber_priority_set ? - parameters->subscriber_priority : 128)); - json_object_set_new(message, "group_order", json_integer(parameters->group_order)); - json_object_set_new(message, "forward", json_integer(parameters->forward)); - json_object_set_new(message, "filter_type", json_integer(parameters->subscription_filter.type)); - if(parameters->subscription_filter.type == IMQUIC_MOQ_FILTER_ABSOLUTE_START || parameters->subscription_filter.type == IMQUIC_MOQ_FILTER_ABSOLUTE_RANGE) { - json_object_set_new(message, "start_group", json_integer(parameters->subscription_filter.start_location.group)); - json_object_set_new(message, "start_object", json_integer(parameters->subscription_filter.start_location.object)); - } - } - json_object_set_new(message, "number_of_parameters", json_integer(params_num)); - imquic_qlog_moq_message_add_request_parameters(message, moq->version, parameters, "parameters"); - imquic_moq_qlog_control_message_created(moq->conn->qlog, moq->control_stream_id, bytes, offset, message); - } -#endif - return offset; -} - -size_t imquic_moq_add_track_status_ok(imquic_moq_context *moq, uint8_t *bytes, size_t blen, uint64_t request_id, - uint64_t track_alias, imquic_moq_request_parameters *parameters) { - if(bytes == NULL || blen < 1) { - IMQUIC_LOG(IMQUIC_LOG_ERR, "[%s][MoQ] Can't add MoQ %s: invalid arguments\n", - imquic_get_connection_name(moq->conn), imquic_moq_message_type_str(IMQUIC_MOQ_TRACK_STATUS_OK, moq->version)); - return 0; - } - size_t offset = 0, len_offset = 0; - IMQUIC_MOQ_ADD_MESSAGE_TYPE(IMQUIC_MOQ_TRACK_STATUS_OK); - offset += imquic_write_varint(request_id, &bytes[offset], blen-offset); - if(moq->version <= IMQUIC_MOQ_VERSION_14) - offset += imquic_write_varint(track_alias, &bytes[offset], blen-offset); - /* For versions older than v15, we need to add some attributes manually, - * but we'll read them from the parameters object the application passed */ - if(moq->version <= IMQUIC_MOQ_VERSION_14) { - offset += imquic_write_varint(parameters->expires, &bytes[offset], blen-offset); - bytes[offset] = parameters->group_order; - offset++; - bytes[offset] = parameters->largest_object_set; - offset++; - if(parameters->largest_object_set) { - offset += imquic_write_varint(parameters->largest_object.group, &bytes[offset], blen-offset); - offset += imquic_write_varint(parameters->largest_object.object, &bytes[offset], blen-offset); - } - } - uint8_t params_num = 0; - offset += imquic_moq_request_parameters_serialize(moq, parameters, &bytes[offset], blen-offset, ¶ms_num); - IMQUIC_MOQ_ADD_MESSAGE_LENGTH(); -#ifdef HAVE_QLOG - if(moq->conn->qlog != NULL && moq->conn->qlog->moq) { - json_t *message = imquic_qlog_moq_message_prepare(moq->version >= IMQUIC_MOQ_VERSION_15 ? "request_ok" : "track_status_ok"); - json_object_set_new(message, "request_id", json_integer(request_id)); - if(moq->version <= IMQUIC_MOQ_VERSION_14) - json_object_set_new(message, "track_alias", json_integer(track_alias)); - if(moq->version <= IMQUIC_MOQ_VERSION_14) { - json_object_set_new(message, "expires", json_integer(parameters->expires)); - json_object_set_new(message, "group_order", json_integer(parameters->group_order)); - json_object_set_new(message, "content_exists", json_integer(parameters->largest_object_set)); - if(parameters->largest_object_set) { - json_object_set_new(message, "largest_group_id", json_integer(parameters->largest_object.group)); - json_object_set_new(message, "largest_object_id", json_integer(parameters->largest_object.object)); - } - } json_object_set_new(message, "number_of_parameters", json_integer(params_num)); imquic_qlog_moq_message_add_request_parameters(message, moq->version, parameters, "parameters"); - imquic_moq_qlog_control_message_created(moq->conn->qlog, moq->control_stream_id, bytes, offset, message); - } -#endif - return offset; -} - -size_t imquic_moq_add_track_status_error(imquic_moq_context *moq, uint8_t *bytes, size_t blen, - uint64_t request_id, imquic_moq_request_error_code error, const char *reason) { - if(bytes == NULL || blen < 1 || (reason && strlen(reason) > 1024)) { - IMQUIC_LOG(IMQUIC_LOG_ERR, "[%s][MoQ] Can't add MoQ %s: invalid arguments\n", - imquic_get_connection_name(moq->conn), imquic_moq_message_type_str(IMQUIC_MOQ_TRACK_STATUS_ERROR, moq->version)); - return 0; - } - if(moq->version < IMQUIC_MOQ_VERSION_15) - error = (imquic_moq_request_error_code)imquic_moq_request_error_code_to_legacy(moq->version, error); - size_t offset = 0, len_offset = 0; - IMQUIC_MOQ_ADD_MESSAGE_TYPE(IMQUIC_MOQ_TRACK_STATUS_ERROR); - offset += imquic_write_varint(request_id, &bytes[offset], blen-offset); - offset += imquic_write_varint(error, &bytes[offset], blen-offset); - size_t reason_len = reason ? strlen(reason) : 0; - offset += imquic_write_varint(reason_len, &bytes[offset], blen-offset); - if(reason_len > 0) { - memcpy(&bytes[offset], reason, reason_len); - offset += reason_len; - } - IMQUIC_MOQ_ADD_MESSAGE_LENGTH(); -#ifdef HAVE_QLOG - if(moq->conn->qlog != NULL && moq->conn->qlog->moq) { - json_t *message = imquic_qlog_moq_message_prepare(moq->version >= IMQUIC_MOQ_VERSION_15 ? "request_error" : "track_status_error"); - json_object_set_new(message, "request_id", json_integer(request_id)); - json_object_set_new(message, "error_code", json_integer(error)); - if(reason != NULL) - json_object_set_new(message, "reason", json_string(reason)); - imquic_moq_qlog_control_message_created(moq->conn->qlog, moq->control_stream_id, bytes, offset, message); + imquic_moq_qlog_control_message_created(moq->conn->qlog, + (moq_stream ? moq_stream->stream_id : moq->control_stream_id), bytes, offset, message); } #endif return offset; } -size_t imquic_moq_add_goaway(imquic_moq_context *moq, uint8_t *bytes, size_t blen, const char *new_session_uri) { +size_t imquic_moq_add_goaway(imquic_moq_context *moq, uint8_t *bytes, size_t blen, const char *new_session_uri, uint64_t timeout) { if(bytes == NULL || blen < 1 || (new_session_uri && strlen(new_session_uri) > 8192)) { IMQUIC_LOG(IMQUIC_LOG_ERR, "[%s][MoQ] Can't add MoQ %s: invalid arguments\n", imquic_get_connection_name(moq->conn), imquic_moq_message_type_str(IMQUIC_MOQ_GOAWAY, moq->version)); @@ -6110,16 +4912,20 @@ size_t imquic_moq_add_goaway(imquic_moq_context *moq, uint8_t *bytes, size_t ble size_t uri_len = new_session_uri ? strlen(new_session_uri) : 0; size_t offset = 0, len_offset = 0; IMQUIC_MOQ_ADD_MESSAGE_TYPE(IMQUIC_MOQ_GOAWAY); - offset += imquic_write_varint(uri_len, &bytes[offset], blen-offset); + offset += imquic_write_moqint(moq->version, uri_len, &bytes[offset], blen-offset); if(uri_len > 0) { memcpy(&bytes[offset], new_session_uri, uri_len); offset += uri_len; } + if(moq->version >= IMQUIC_MOQ_VERSION_17) + offset += imquic_write_moqint(moq->version, timeout, &bytes[offset], blen-offset); IMQUIC_MOQ_ADD_MESSAGE_LENGTH(); #ifdef HAVE_QLOG if(moq->conn->qlog != NULL && moq->conn->qlog->moq) { json_t *message = imquic_qlog_moq_message_prepare("goaway"); imquic_qlog_event_add_raw(message, "new_session_uri", (uint8_t *)new_session_uri, uri_len); + if(moq->version >= IMQUIC_MOQ_VERSION_17) + json_object_set_new(message, "timeout", json_integer(timeout)); imquic_moq_qlog_control_message_created(moq->conn->qlog, moq->control_stream_id, bytes, offset, message); } #endif @@ -6128,34 +4934,33 @@ size_t imquic_moq_add_goaway(imquic_moq_context *moq, uint8_t *bytes, size_t ble size_t imquic_moq_add_object_datagram(imquic_moq_context *moq, uint8_t *bytes, size_t blen, uint64_t request_id, uint64_t track_alias, uint64_t group_id, uint64_t object_id, uint64_t object_status, uint8_t priority, - uint8_t *payload, size_t plen, uint8_t *extensions, size_t elen) { + uint8_t *payload, size_t plen, uint8_t *properties, size_t prlen) { if(bytes == NULL || blen < 1) { IMQUIC_LOG(IMQUIC_LOG_ERR, "[%s][MoQ] Can't add MoQ %s: invalid arguments\n", imquic_get_connection_name(moq->conn), imquic_moq_datagram_message_type_str(IMQUIC_MOQ_OBJECT_DATAGRAM, moq->version)); return 0; } /* TODO Involve EOG */ - gboolean has_ext = (extensions != NULL && elen > 0), is_eog = FALSE; - gboolean has_oid = (moq->version < IMQUIC_MOQ_VERSION_14 || - (moq->version >= IMQUIC_MOQ_VERSION_14 && object_id != 0)); + gboolean has_prop = (properties != NULL && prlen > 0), is_eog = FALSE; + gboolean has_oid = (object_id != 0); gboolean has_priority = TRUE; /* FIXME */ imquic_moq_datagram_message_type dtype = imquic_moq_datagram_message_type_return(moq->version, TRUE, /* Payload */ - has_ext, /* Extensions */ + has_prop, /* Properties */ is_eog, /* End of Group */ has_oid, /* Object ID */ has_priority); /* Priority */ - size_t offset = imquic_write_varint(dtype, bytes, blen); - offset += imquic_write_varint(track_alias, &bytes[offset], blen-offset); - offset += imquic_write_varint(group_id, &bytes[offset], blen-offset); - if(moq->version < IMQUIC_MOQ_VERSION_14 || has_oid) - offset += imquic_write_varint(object_id, &bytes[offset], blen-offset); + size_t offset = imquic_write_moqint(moq->version, dtype, bytes, blen); + offset += imquic_write_moqint(moq->version, track_alias, &bytes[offset], blen-offset); + offset += imquic_write_moqint(moq->version, group_id, &bytes[offset], blen-offset); + if(has_oid) + offset += imquic_write_moqint(moq->version, object_id, &bytes[offset], blen-offset); if(has_priority) { bytes[offset] = priority; offset++; } - if(has_ext) - offset += imquic_moq_add_object_extensions(moq, &bytes[offset], blen-offset, extensions, elen); + if(has_prop) + offset += imquic_moq_add_properties(moq, &bytes[offset], blen-offset, properties, prlen); if(payload != NULL && plen > 0) { memcpy(&bytes[offset], payload, plen); offset += plen; @@ -6165,34 +4970,33 @@ size_t imquic_moq_add_object_datagram(imquic_moq_context *moq, uint8_t *bytes, s size_t imquic_moq_add_object_datagram_status(imquic_moq_context *moq, uint8_t *bytes, size_t blen, uint64_t track_alias, uint64_t group_id, uint64_t object_id, uint8_t priority, - uint64_t object_status, uint8_t *extensions, size_t elen) { + uint64_t object_status, uint8_t *properties, size_t prlen) { if(bytes == NULL || blen < 1) { IMQUIC_LOG(IMQUIC_LOG_ERR, "[%s][MoQ] Can't add MoQ %s: invalid arguments\n", imquic_get_connection_name(moq->conn), imquic_moq_datagram_message_type_str(IMQUIC_MOQ_OBJECT_DATAGRAM_STATUS, moq->version)); return 0; } - gboolean has_ext = (extensions != NULL && elen > 0); - gboolean has_oid = (moq->version < IMQUIC_MOQ_VERSION_14 || - (moq->version >= IMQUIC_MOQ_VERSION_14 && object_id != 0)); + gboolean has_prop = (properties != NULL && prlen > 0); + gboolean has_oid = (object_id != 0); gboolean has_priority = TRUE; /* FIXME */ imquic_moq_datagram_message_type dtype = imquic_moq_datagram_message_type_return(moq->version, FALSE, /* Status */ - has_ext, /* Extensions */ + has_prop, /* Properties */ FALSE, /* End of Group */ has_oid, /* Object ID */ has_priority); /* Priority */ - size_t offset = imquic_write_varint(dtype, bytes, blen); - offset += imquic_write_varint(track_alias, &bytes[offset], blen-offset); - offset += imquic_write_varint(group_id, &bytes[offset], blen-offset); + size_t offset = imquic_write_moqint(moq->version, dtype, bytes, blen); + offset += imquic_write_moqint(moq->version, track_alias, &bytes[offset], blen-offset); + offset += imquic_write_moqint(moq->version, group_id, &bytes[offset], blen-offset); if(has_oid) - offset += imquic_write_varint(object_id, &bytes[offset], blen-offset); + offset += imquic_write_moqint(moq->version, object_id, &bytes[offset], blen-offset); if(has_priority) { bytes[offset] = priority; offset++; } - if(has_ext) - offset += imquic_moq_add_object_extensions(moq, &bytes[offset], blen-offset, extensions, elen); - offset += imquic_write_varint(object_status, &bytes[offset], blen-offset); + if(has_prop) + offset += imquic_moq_add_properties(moq, &bytes[offset], blen-offset, properties, prlen); + offset += imquic_write_moqint(moq->version, object_status, &bytes[offset], blen-offset); return offset; } @@ -6206,11 +5010,11 @@ size_t imquic_moq_add_subgroup_header(imquic_moq_context *moq, imquic_moq_stream imquic_moq_data_message_type dtype = moq_stream->type; gboolean has_sg = FALSE, has_priority = FALSE; imquic_moq_data_message_type_to_subgroup_header(moq->version, moq_stream->type, &has_sg, NULL, NULL, NULL, &has_priority, NULL); - size_t offset = imquic_write_varint(dtype, bytes, blen); - offset += imquic_write_varint(track_alias, &bytes[offset], blen-offset); - offset += imquic_write_varint(group_id, &bytes[offset], blen-offset); + size_t offset = imquic_write_moqint(moq->version, dtype, bytes, blen); + offset += imquic_write_moqint(moq->version, track_alias, &bytes[offset], blen-offset); + offset += imquic_write_moqint(moq->version, group_id, &bytes[offset], blen-offset); if(has_sg) - offset += imquic_write_varint(subgroup_id, &bytes[offset], blen-offset); + offset += imquic_write_moqint(moq->version, subgroup_id, &bytes[offset], blen-offset); if(has_sg) { bytes[offset] = priority; offset++; @@ -6220,25 +5024,25 @@ size_t imquic_moq_add_subgroup_header(imquic_moq_context *moq, imquic_moq_stream size_t imquic_moq_add_subgroup_header_object(imquic_moq_context *moq, imquic_moq_stream *moq_stream, uint8_t *bytes, size_t blen, uint64_t object_id, uint64_t object_status, - uint8_t *payload, size_t plen, uint8_t *extensions, size_t elen) { + uint8_t *payload, size_t plen, uint8_t *properties, size_t prlen) { if(bytes == NULL || blen < 1) { IMQUIC_LOG(IMQUIC_LOG_ERR, "[%s][MoQ] Can't add MoQ %s object: invalid arguments\n", imquic_get_connection_name(moq->conn), imquic_moq_data_message_type_str(IMQUIC_MOQ_SUBGROUP_HEADER, moq->version)); return 0; } size_t offset = 0; - offset += imquic_write_varint(object_id, &bytes[offset], blen-offset); + offset += imquic_write_moqint(moq->version, object_id, &bytes[offset], blen-offset); /* TODO We can optimize this by only doing it once, when we parse the header */ /* TODO Involve EOG too */ - gboolean has_ext = FALSE; - imquic_moq_data_message_type_to_subgroup_header(moq->version, moq_stream->type, NULL, NULL, &has_ext, NULL, NULL, NULL); - if(has_ext) - offset += imquic_moq_add_object_extensions(moq, &bytes[offset], blen-offset, extensions, elen); + gboolean has_prop = FALSE; + imquic_moq_data_message_type_to_subgroup_header(moq->version, moq_stream->type, NULL, NULL, &has_prop, NULL, NULL, NULL); + if(has_prop) + offset += imquic_moq_add_properties(moq, &bytes[offset], blen-offset, properties, prlen); if(payload == NULL) plen = 0; - offset += imquic_write_varint(plen, &bytes[offset], blen-offset); + offset += imquic_write_moqint(moq->version, plen, &bytes[offset], blen-offset); if(plen == 0) - offset += imquic_write_varint(object_status, &bytes[offset], blen-offset); + offset += imquic_write_moqint(moq->version, object_status, &bytes[offset], blen-offset); if(plen > 0) { memcpy(&bytes[offset], payload, plen); offset += plen; @@ -6252,14 +5056,14 @@ size_t imquic_moq_add_fetch_header(imquic_moq_context *moq, uint8_t *bytes, size imquic_get_connection_name(moq->conn), imquic_moq_data_message_type_str(IMQUIC_MOQ_FETCH_HEADER, moq->version)); return 0; } - size_t offset = imquic_write_varint(IMQUIC_MOQ_FETCH_HEADER, bytes, blen); - offset += imquic_write_varint(request_id, &bytes[offset], blen-offset); + size_t offset = imquic_write_moqint(moq->version, IMQUIC_MOQ_FETCH_HEADER, bytes, blen); + offset += imquic_write_moqint(moq->version, request_id, &bytes[offset], blen-offset); return offset; } size_t imquic_moq_add_fetch_header_object(imquic_moq_context *moq, uint8_t *bytes, size_t blen, uint64_t flags, uint64_t group_id, uint64_t subgroup_id, uint64_t object_id, uint8_t priority, - uint64_t object_status, uint8_t *payload, size_t plen, uint8_t *extensions, size_t elen) { + uint64_t object_status, uint8_t *payload, size_t plen, uint8_t *properties, size_t prlen) { if(bytes == NULL || blen < 1) { IMQUIC_LOG(IMQUIC_LOG_ERR, "[%s][MoQ] Can't add MoQ %s object: invalid arguments\n", imquic_get_connection_name(moq->conn), imquic_moq_data_message_type_str(IMQUIC_MOQ_FETCH_HEADER, moq->version)); @@ -6267,32 +5071,27 @@ size_t imquic_moq_add_fetch_header_object(imquic_moq_context *moq, uint8_t *byte } size_t offset = 0; imquic_moq_fetch_subgroup_type subgroup_type = IMQUIC_MOQ_FETCH_SUBGROUP_ID; - gboolean has_oid = FALSE, has_group = FALSE, has_priority = FALSE, has_ext = FALSE, is_datagram = FALSE; + gboolean has_oid = FALSE, has_group = FALSE, has_priority = FALSE, has_prop = FALSE, is_datagram = FALSE; imquic_moq_parse_fetch_serialization_flags(moq->version, flags, - &subgroup_type, &has_oid, &has_group, &has_priority, &has_ext, &is_datagram, NULL, NULL, NULL); - if(moq->version == IMQUIC_MOQ_VERSION_15) { - bytes[offset] = flags; - offset++; - } else if(moq->version >= IMQUIC_MOQ_VERSION_16) { - offset += imquic_write_varint(flags, &bytes[offset], blen-offset); - } + &subgroup_type, &has_oid, &has_group, &has_priority, &has_prop, &is_datagram, NULL, NULL, NULL); + offset += imquic_write_moqint(moq->version, flags, &bytes[offset], blen-offset); if(has_group) - offset += imquic_write_varint(group_id, &bytes[offset], blen-offset); + offset += imquic_write_moqint(moq->version, group_id, &bytes[offset], blen-offset); if(subgroup_type == IMQUIC_MOQ_FETCH_SUBGROUP_ID && !is_datagram) - offset += imquic_write_varint(subgroup_id, &bytes[offset], blen-offset); + offset += imquic_write_moqint(moq->version, subgroup_id, &bytes[offset], blen-offset); if(has_oid) - offset += imquic_write_varint(object_id, &bytes[offset], blen-offset); + offset += imquic_write_moqint(moq->version, object_id, &bytes[offset], blen-offset); if(has_priority) { bytes[offset] = priority; offset++; } - if(has_ext) - offset += imquic_moq_add_object_extensions(moq, &bytes[offset], blen-offset, extensions, elen); + if(has_prop) + offset += imquic_moq_add_properties(moq, &bytes[offset], blen-offset, properties, prlen); if(payload == NULL) plen = 0; - offset += imquic_write_varint(plen, &bytes[offset], blen-offset); + offset += imquic_write_moqint(moq->version, plen, &bytes[offset], blen-offset); if(plen == 0) - offset += imquic_write_varint(object_status, &bytes[offset], blen-offset); + offset += imquic_write_moqint(moq->version, object_status, &bytes[offset], blen-offset); if(plen > 0) { memcpy(&bytes[offset], payload, plen); offset += plen; @@ -6300,74 +5099,70 @@ size_t imquic_moq_add_fetch_header_object(imquic_moq_context *moq, uint8_t *byte return offset; } -size_t imquic_moq_add_object_extensions(imquic_moq_context *moq, uint8_t *bytes, size_t blen, - uint8_t *extensions, size_t elen) { +size_t imquic_moq_add_properties(imquic_moq_context *moq, uint8_t *bytes, size_t blen, + uint8_t *properties, size_t prlen) { if(bytes == NULL || blen < 1) { - IMQUIC_LOG(IMQUIC_LOG_WARN, "[%s][MoQ] Can't serialize MoQ object extensions: invalid arguments\n", + IMQUIC_LOG(IMQUIC_LOG_WARN, "[%s][MoQ] Can't serialize MoQ properties: invalid arguments\n", imquic_get_connection_name(moq->conn)); return 0; } - if(extensions == NULL || elen == 0) { - extensions = NULL; - elen = 0; + if(properties == NULL || prlen == 0) { + properties = NULL; + prlen = 0; } size_t offset = 0; - offset += imquic_write_varint(elen, &bytes[offset], blen-offset); - if(extensions != NULL && elen > 0) { - memcpy(&bytes[offset], extensions, elen); - offset += elen; + offset += imquic_write_moqint(moq->version, prlen, &bytes[offset], blen-offset); + if(properties != NULL && prlen > 0) { + memcpy(&bytes[offset], properties, prlen); + offset += prlen; } return offset; } -/* Adding parameters to a buffer */ -size_t imquic_moq_parameter_add_int(imquic_moq_context *moq, uint8_t *bytes, size_t blen, +/* Adding and parsing setup options and parameters to a buffer */ +size_t imquic_moq_setup_option_add_int(imquic_moq_context *moq, uint8_t *bytes, size_t blen, uint64_t param, uint64_t prev, uint64_t number) { if(bytes == NULL || blen == 0) { - IMQUIC_LOG(IMQUIC_LOG_ERR, "[%s][MoQ] Can't add MoQ numeric parameter %"SCNu64": invalid arguments\n", + IMQUIC_LOG(IMQUIC_LOG_ERR, "[%s][MoQ] Can't add MoQ numeric setup option %"SCNu64": invalid arguments\n", imquic_get_connection_name(moq->conn), param); return 0; } if(param % 2 != 0) { - IMQUIC_LOG(IMQUIC_LOG_ERR, "[%s][MoQ] Can't add MoQ numeric parameter %"SCNu64": type is odd\n", + IMQUIC_LOG(IMQUIC_LOG_ERR, "[%s][MoQ] Can't add MoQ numeric setup option %"SCNu64": type is odd\n", imquic_get_connection_name(moq->conn), param); return 0; } - if(moq->version >= IMQUIC_MOQ_VERSION_16) { - if(prev > param) { - IMQUIC_LOG(IMQUIC_LOG_ERR, "[%s][MoQ] Can't add MoQ numeric parameter %"SCNu64": previous parameter %"SCNu64" for delta-encoding is larger\n", - imquic_get_connection_name(moq->conn), param, prev); - return 0; - } - param -= prev; + if(prev > param) { + IMQUIC_LOG(IMQUIC_LOG_ERR, "[%s][MoQ] Can't add MoQ numeric setup option %"SCNu64": previous setup option %"SCNu64" for delta-encoding is larger\n", + imquic_get_connection_name(moq->conn), param, prev); + return 0; } - size_t offset = imquic_write_varint(param, &bytes[0], blen); - offset += imquic_write_varint(number, &bytes[offset], blen-offset); + param -= prev; + size_t offset = imquic_write_moqint(moq->version, param, &bytes[0], blen); + offset += imquic_write_moqint(moq->version, number, &bytes[offset], blen-offset); return offset; } -size_t imquic_moq_parameter_add_data(imquic_moq_context *moq, uint8_t *bytes, size_t blen, +size_t imquic_moq_setup_option_add_data(imquic_moq_context *moq, uint8_t *bytes, size_t blen, uint64_t param, uint64_t prev, uint8_t *buf, size_t buflen) { if(bytes == NULL || blen == 0 || (buflen > 0 && buf == 0) || buflen > UINT16_MAX) { - IMQUIC_LOG(IMQUIC_LOG_ERR, "[%s][MoQ] Can't add MoQ data parameter %"SCNu64": invalid arguments\n", + IMQUIC_LOG(IMQUIC_LOG_ERR, "[%s][MoQ] Can't add MoQ data setup option %"SCNu64": invalid arguments\n", imquic_get_connection_name(moq->conn), param); return 0; } if(param % 2 != 1) { - IMQUIC_LOG(IMQUIC_LOG_ERR, "[%s][MoQ] Can't add MoQ data parameter %"SCNu64": type is even\n", + IMQUIC_LOG(IMQUIC_LOG_ERR, "[%s][MoQ] Can't add MoQ data setup option %"SCNu64": type is even\n", imquic_get_connection_name(moq->conn), param); return 0; } - if(moq->version >= IMQUIC_MOQ_VERSION_16) { - if(prev > param) { - IMQUIC_LOG(IMQUIC_LOG_ERR, "[%s][MoQ] Can't add MoQ data parameter %"SCNu64": previous parameter %"SCNu64" for delta-encoding is larger\n", - imquic_get_connection_name(moq->conn), param, prev); - return 0; - } - param -= prev; + if(prev > param) { + IMQUIC_LOG(IMQUIC_LOG_ERR, "[%s][MoQ] Can't add MoQ data setup option %"SCNu64": previous setup option %"SCNu64" for delta-encoding is larger\n", + imquic_get_connection_name(moq->conn), param, prev); + return 0; } - size_t offset = imquic_write_varint(param, &bytes[0], blen); - offset += imquic_write_varint(buflen, &bytes[offset], blen); + param -= prev; + size_t offset = imquic_write_moqint(moq->version, param, &bytes[0], blen); + offset += imquic_write_moqint(moq->version, buflen, &bytes[offset], blen); if(buflen > 0) { memcpy(&bytes[offset], buf, buflen); offset += buflen; @@ -6375,8 +5170,8 @@ size_t imquic_moq_parameter_add_data(imquic_moq_context *moq, uint8_t *bytes, si return offset; } -size_t imquic_moq_parse_setup_parameter(imquic_moq_context *moq, uint8_t *bytes, size_t blen, - imquic_moq_setup_parameters *params, uint64_t *param_type, uint8_t *error) { +size_t imquic_moq_parse_setup_option(imquic_moq_context *moq, uint8_t *bytes, size_t blen, + imquic_moq_setup_options *params, uint64_t *param_type, uint8_t *error) { if(error) *error = IMQUIC_MOQ_UNKNOWN_ERROR; if(bytes == NULL || blen < 1) { @@ -6386,43 +5181,42 @@ size_t imquic_moq_parse_setup_parameter(imquic_moq_context *moq, uint8_t *bytes, } size_t offset = 0; uint8_t length = 0; - uint64_t type = imquic_read_varint(&bytes[offset], blen-offset, &length); + uint64_t type = imquic_read_moqint(moq->version, &bytes[offset], blen-offset, &length); IMQUIC_MOQ_CHECK_ERR(length == 0 || length >= blen-offset, NULL, 0, 0, "Broken MoQ setup parameter"); offset += length; - if(moq->version >= IMQUIC_MOQ_VERSION_16) - type += *param_type; + type += *param_type; *param_type = type; IMQUIC_LOG(IMQUIC_MOQ_LOG_HUGE, "[%s][MoQ] -- -- %s (%"SCNu64")\n", - imquic_get_connection_name(moq->conn), imquic_moq_setup_parameter_type_str(type), type); + imquic_get_connection_name(moq->conn), imquic_moq_setup_option_type_str(type), type); uint64_t len = 0; if(type % 2 == 1) { - len = imquic_read_varint(&bytes[offset], blen-offset, &length); + len = imquic_read_moqint(moq->version, &bytes[offset], blen-offset, &length); IMQUIC_MOQ_CHECK_ERR(length == 0, NULL, 0, 0, "Broken MoQ setup parameter"); offset += length; IMQUIC_MOQ_CHECK_ERR(len > blen-offset, NULL, 0, 0, "Broken MoQ setup parameter"); } /* Update the parsed parameter */ - if(type == IMQUIC_MOQ_SETUP_PARAM_PATH) { + if(type == IMQUIC_MOQ_SETUP_OPTION_PATH) { params->path_set = TRUE; if(len > 0) g_snprintf(params->path, sizeof(params->path), "%.*s", (int)len, &bytes[offset]); IMQUIC_LOG(IMQUIC_MOQ_LOG_HUGE, "[%s][MoQ] -- -- -- '%s'\n", imquic_get_connection_name(moq->conn), params->path); - } else if(type == IMQUIC_MOQ_SETUP_PARAM_MAX_REQUEST_ID) { - params->max_request_id = imquic_read_varint(&bytes[offset], blen-offset, &length); + } else if(type == IMQUIC_MOQ_SETUP_OPTION_MAX_REQUEST_ID) { + params->max_request_id = imquic_read_moqint(moq->version, &bytes[offset], blen-offset, &length); IMQUIC_MOQ_CHECK_ERR(length == 0 || len > blen-offset, NULL, 0, 0, "Broken MoQ setup parameter"); params->max_request_id_set = TRUE; IMQUIC_LOG(IMQUIC_MOQ_LOG_HUGE, "[%s][MoQ] -- -- -- %"SCNu64"\n", imquic_get_connection_name(moq->conn), params->max_request_id); len = length; - } else if(type == IMQUIC_MOQ_SETUP_PARAM_MAX_AUTH_TOKEN_CACHE_SIZE) { - params->max_auth_token_cache_size = imquic_read_varint(&bytes[offset], blen-offset, &length); + } else if(type == IMQUIC_MOQ_SETUP_OPTION_MAX_AUTH_TOKEN_CACHE_SIZE) { + params->max_auth_token_cache_size = imquic_read_moqint(moq->version, &bytes[offset], blen-offset, &length); IMQUIC_MOQ_CHECK_ERR(length == 0 || len > blen-offset, NULL, 0, 0, "Broken MoQ setup parameter"); params->max_auth_token_cache_size_set = TRUE; IMQUIC_LOG(IMQUIC_MOQ_LOG_HUGE, "[%s][MoQ] -- -- -- %"SCNu64"\n", imquic_get_connection_name(moq->conn), params->max_request_id); len = length; - } else if(type == IMQUIC_MOQ_SETUP_PARAM_AUTHORIZATION_TOKEN && moq->version >= IMQUIC_MOQ_VERSION_12) { + } else if(type == IMQUIC_MOQ_SETUP_OPTION_AUTHORIZATION_TOKEN) { params->auth_token_set = TRUE; size_t auth_len = len; if(auth_len > sizeof(params->auth_token)) { @@ -6435,18 +5229,25 @@ size_t imquic_moq_parse_setup_parameter(imquic_moq_context *moq, uint8_t *bytes, char ai_str[513]; IMQUIC_LOG(IMQUIC_MOQ_LOG_HUGE, "[%s][MoQ] -- -- -- %s\n", imquic_get_connection_name(moq->conn), imquic_hex_str(&bytes[offset], auth_len, ai_str, sizeof(ai_str))); - } else if(type == IMQUIC_MOQ_SETUP_PARAM_AUTHORITY && moq->version >= IMQUIC_MOQ_VERSION_14) { + } else if(type == IMQUIC_MOQ_SETUP_OPTION_AUTHORITY) { params->authority_set = TRUE; if(len > 0) g_snprintf(params->authority, sizeof(params->authority), "%.*s", (int)len, &bytes[offset]); IMQUIC_LOG(IMQUIC_MOQ_LOG_HUGE, "[%s][MoQ] -- -- -- '%s'\n", imquic_get_connection_name(moq->conn), params->authority); - } else if(type == IMQUIC_MOQ_SETUP_PARAM_MOQT_IMPLEMENTATION && moq->version >= IMQUIC_MOQ_VERSION_14) { + } else if(type == IMQUIC_MOQ_SETUP_OPTION_MOQT_IMPLEMENTATION) { params->moqt_implementation_set = TRUE; if(len > 0) g_snprintf(params->moqt_implementation, sizeof(params->moqt_implementation), "%.*s", (int)len, &bytes[offset]); IMQUIC_LOG(IMQUIC_MOQ_LOG_HUGE, "[%s][MoQ] -- -- -- '%s'\n", imquic_get_connection_name(moq->conn), params->moqt_implementation); + } else if(moq->version >= IMQUIC_MOQ_VERSION_17 && imquic_moq_is_grease(type)) { + /* This is a GREASE setup option, just skip it */ + if(type % 2 == 0) { + (void)imquic_read_moqint(moq->version, &bytes[offset], blen-offset, &length); + IMQUIC_MOQ_CHECK_ERR(length == 0 || len > blen-offset, NULL, 0, 0, "Broken MoQ setup parameter"); + len = length; + } } else { IMQUIC_LOG(IMQUIC_LOG_WARN, "[%s][MoQ] Unsupported parameter '%"SCNu64"'\n", imquic_get_connection_name(moq->conn), type); @@ -6460,6 +5261,99 @@ size_t imquic_moq_parse_setup_parameter(imquic_moq_context *moq, uint8_t *bytes, return offset; } +size_t imquic_moq_parameter_add_varint(imquic_moq_context *moq, uint8_t *bytes, size_t blen, + uint64_t param, uint64_t prev, uint64_t number) { + if(bytes == NULL || blen == 0) { + IMQUIC_LOG(IMQUIC_LOG_ERR, "[%s][MoQ] Can't add MoQ varint parameter %"SCNu64": invalid arguments\n", + imquic_get_connection_name(moq->conn), param); + return 0; + } + if(moq->version <= IMQUIC_MOQ_VERSION_16 && param % 2 != 0) { + IMQUIC_LOG(IMQUIC_LOG_ERR, "[%s][MoQ] Can't add MoQ varint parameter %"SCNu64": type is odd\n", + imquic_get_connection_name(moq->conn), param); + return 0; + } + if(prev > param) { + IMQUIC_LOG(IMQUIC_LOG_ERR, "[%s][MoQ] Can't add MoQ varint parameter %"SCNu64": previous parameter %"SCNu64" for delta-encoding is larger\n", + imquic_get_connection_name(moq->conn), param, prev); + return 0; + } + param -= prev; + size_t offset = 0; + offset += imquic_write_moqint(moq->version, param, &bytes[offset], blen-offset); + offset += imquic_write_moqint(moq->version, number, &bytes[offset], blen-offset); + return offset; +} + +size_t imquic_moq_parameter_add_uint8(imquic_moq_context *moq, uint8_t *bytes, size_t blen, + uint64_t param, uint64_t prev, uint8_t number) { + if(moq->version <= IMQUIC_MOQ_VERSION_16) + return imquic_moq_parameter_add_varint(moq, bytes, blen, param, prev, number); + if(bytes == NULL || blen == 0) { + IMQUIC_LOG(IMQUIC_LOG_ERR, "[%s][MoQ] Can't add MoQ byte parameter %"SCNu64": invalid arguments\n", + imquic_get_connection_name(moq->conn), param); + return 0; + } + if(prev > param) { + IMQUIC_LOG(IMQUIC_LOG_ERR, "[%s][MoQ] Can't add MoQ byte parameter %"SCNu64": previous parameter %"SCNu64" for delta-encoding is larger\n", + imquic_get_connection_name(moq->conn), param, prev); + return 0; + } + param -= prev; + size_t offset = imquic_write_moqint(moq->version, param, bytes, blen); + bytes[offset] = number; + return offset+1; +} + +size_t imquic_moq_parameter_add_location(imquic_moq_context *moq, uint8_t *bytes, size_t blen, + uint64_t param, uint64_t prev, imquic_moq_location *location) { + if(bytes == NULL || blen == 0 || location == NULL) { + IMQUIC_LOG(IMQUIC_LOG_ERR, "[%s][MoQ] Can't add MoQ location parameter %"SCNu64": invalid arguments\n", + imquic_get_connection_name(moq->conn), param); + return 0; + } + if(moq->version <= IMQUIC_MOQ_VERSION_16) { + uint8_t temp[40]; + size_t tlen = sizeof(temp); + size_t toffset = imquic_write_moqint(moq->version, location->group, temp, tlen); + toffset += imquic_write_moqint(moq->version, location->object, &temp[toffset], tlen-toffset); + return imquic_moq_parameter_add_data(moq, bytes, blen, param, prev, temp, toffset); + } + param -= prev; + size_t offset = 0; + offset += imquic_write_moqint(moq->version, param, &bytes[offset], blen-offset); + offset += imquic_write_moqint(moq->version, location->group, &bytes[offset], blen-offset); + offset += imquic_write_moqint(moq->version, location->object, &bytes[offset], blen-offset); + return offset; +} + +size_t imquic_moq_parameter_add_data(imquic_moq_context *moq, uint8_t *bytes, size_t blen, + uint64_t param, uint64_t prev, uint8_t *buf, size_t buflen) { + if(bytes == NULL || blen == 0 || (buflen > 0 && buf == 0) || buflen > UINT16_MAX) { + IMQUIC_LOG(IMQUIC_LOG_ERR, "[%s][MoQ] Can't add MoQ data parameter %"SCNu64": invalid arguments\n", + imquic_get_connection_name(moq->conn), param); + return 0; + } + if(moq->version <= IMQUIC_MOQ_VERSION_16 && param % 2 != 1) { + IMQUIC_LOG(IMQUIC_LOG_ERR, "[%s][MoQ] Can't add MoQ data parameter %"SCNu64": type is even\n", + imquic_get_connection_name(moq->conn), param); + return 0; + } + if(prev > param) { + IMQUIC_LOG(IMQUIC_LOG_ERR, "[%s][MoQ] Can't add MoQ data parameter %"SCNu64": previous parameter %"SCNu64" for delta-encoding is larger\n", + imquic_get_connection_name(moq->conn), param, prev); + return 0; + } + param -= prev; + size_t offset = imquic_write_moqint(moq->version, param, &bytes[0], blen); + offset += imquic_write_moqint(moq->version, buflen, &bytes[offset], blen); + if(buflen > 0) { + memcpy(&bytes[offset], buf, buflen); + offset += buflen; + } + return offset; +} + size_t imquic_moq_parse_request_parameter(imquic_moq_context *moq, uint8_t *bytes, size_t blen, imquic_moq_request_parameters *params, uint64_t *param_type, uint8_t *error) { if(error) @@ -6471,24 +5365,29 @@ size_t imquic_moq_parse_request_parameter(imquic_moq_context *moq, uint8_t *byte } size_t offset = 0; uint8_t length = 0; - uint64_t type = imquic_read_varint(&bytes[offset], blen-offset, &length); + uint64_t type = imquic_read_moqint(moq->version, &bytes[offset], blen-offset, &length); IMQUIC_MOQ_CHECK_ERR(length == 0 || length >= blen-offset, NULL, 0, 0, "Broken MoQ request parameter"); offset += length; - if(moq->version >= IMQUIC_MOQ_VERSION_16) - type += *param_type; + type += *param_type; *param_type = type; IMQUIC_LOG(IMQUIC_MOQ_LOG_HUGE, "[%s][MoQ] -- -- %s (%"SCNu64")\n", imquic_get_connection_name(moq->conn), imquic_moq_request_parameter_type_str(type, moq->version), type); uint64_t len = 0; - if(type % 2 == 1) { - len = imquic_read_varint(&bytes[offset], blen-offset, &length); + /* Parameters are formatted differently, depending on the version: + * older versions used TLV, while newer versions have hardcoded + * mappings between known parameters and the types to parse them as. + * As such, we only read the length if it's an older version and + * TLS tells us so, or for newer versions for params that need it */ + if((moq->version <= IMQUIC_MOQ_VERSION_16 && type % 2 == 1) || + (moq->version >= IMQUIC_MOQ_VERSION_17 && (type == IMQUIC_MOQ_REQUEST_PARAM_AUTHORIZATION_TOKEN || + type == IMQUIC_MOQ_REQUEST_PARAM_SUBSCRIPTION_FILTER))) { + len = imquic_read_moqint(moq->version, &bytes[offset], blen-offset, &length); IMQUIC_MOQ_CHECK_ERR(length == 0 || length >= blen-offset, NULL, 0, 0, "Broken MoQ request parameter"); offset += length; IMQUIC_MOQ_CHECK_ERR(len > blen-offset, NULL, 0, 0, "Broken MoQ request parameter"); } /* Update the parsed parameter */ - if((moq->version >= IMQUIC_MOQ_VERSION_12 && type == IMQUIC_MOQ_REQUEST_PARAM_AUTHORIZATION_TOKEN) || - (moq->version == IMQUIC_MOQ_VERSION_11 && type == IMQUIC_MOQ_REQUEST_PARAM_AUTHORIZATION_TOKEN_v11)) { + if(type == IMQUIC_MOQ_REQUEST_PARAM_AUTHORIZATION_TOKEN) { params->auth_token_set = TRUE; size_t auth_len = len; if(auth_len > sizeof(params->auth_token)) { @@ -6502,111 +5401,134 @@ size_t imquic_moq_parse_request_parameter(imquic_moq_context *moq, uint8_t *byte IMQUIC_LOG(IMQUIC_MOQ_LOG_HUGE, "[%s][MoQ] -- -- -- %s\n", imquic_get_connection_name(moq->conn), imquic_hex_str(&bytes[offset], auth_len, ai_str, sizeof(ai_str))); } else if(type == IMQUIC_MOQ_REQUEST_PARAM_DELIVERY_TIMEOUT) { - params->delivery_timeout = imquic_read_varint(&bytes[offset], blen-offset, &length); + params->delivery_timeout = imquic_read_moqint(moq->version, &bytes[offset], blen-offset, &length); IMQUIC_MOQ_CHECK_ERR(length == 0 || params->delivery_timeout == 0, NULL, 0, 0, "Broken MoQ request parameter"); params->delivery_timeout_set = TRUE; IMQUIC_LOG(IMQUIC_MOQ_LOG_HUGE, "[%s][MoQ] -- -- -- %"SCNu64"\n", imquic_get_connection_name(moq->conn), params->delivery_timeout); len = length; - } else if(type == IMQUIC_MOQ_REQUEST_PARAM_MAX_CACHE_DURATION && moq->version <= IMQUIC_MOQ_VERSION_15) { - params->max_cache_duration = imquic_read_varint(&bytes[offset], blen-offset, &length); - IMQUIC_MOQ_CHECK_ERR(length == 0, NULL, 0, 0, "Broken MoQ request parameter"); - params->max_cache_duration_set = TRUE; + } else if(type == IMQUIC_MOQ_REQUEST_PARAM_RENDEZVOUS_TIMEOUT) { + params->rendezvous_timeout = imquic_read_moqint(moq->version, &bytes[offset], blen-offset, &length); + IMQUIC_MOQ_CHECK_ERR(length == 0 || params->rendezvous_timeout == 0, NULL, 0, 0, "Broken MoQ request parameter"); + params->rendezvous_timeout_set = TRUE; IMQUIC_LOG(IMQUIC_MOQ_LOG_HUGE, "[%s][MoQ] -- -- -- %"SCNu64"\n", - imquic_get_connection_name(moq->conn), params->max_cache_duration); - len = length; - } else if(moq->version == IMQUIC_MOQ_VERSION_15 && type == IMQUIC_MOQ_REQUEST_PARAM_PUBLISHER_PRIORITY) { - uint64_t publisher_priority = imquic_read_varint(&bytes[offset], blen-offset, &length); - IMQUIC_MOQ_CHECK_ERR(length == 0 || publisher_priority > 255, NULL, 0, 0, "Broken MoQ request parameter"); - params->publisher_priority = publisher_priority; - params->publisher_priority_set = TRUE; - IMQUIC_LOG(IMQUIC_MOQ_LOG_HUGE, "[%s][MoQ] -- -- -- %"SCNu8"\n", - imquic_get_connection_name(moq->conn), params->publisher_priority); + imquic_get_connection_name(moq->conn), params->rendezvous_timeout); len = length; - } else if(moq->version >= IMQUIC_MOQ_VERSION_15 && type == IMQUIC_MOQ_REQUEST_PARAM_SUBSCRIBER_PRIORITY) { - uint64_t subscriber_priority = imquic_read_varint(&bytes[offset], blen-offset, &length); - IMQUIC_MOQ_CHECK_ERR(length == 0 || subscriber_priority > 255, NULL, 0, 0, "Broken MoQ request parameter"); + } else if(type == IMQUIC_MOQ_REQUEST_PARAM_SUBSCRIBER_PRIORITY) { + uint64_t subscriber_priority = 0; + if(moq->version <= IMQUIC_MOQ_VERSION_16) { + subscriber_priority = imquic_read_moqint(moq->version, &bytes[offset], blen-offset, &length); + IMQUIC_MOQ_CHECK_ERR(length == 0 || subscriber_priority > 255, NULL, 0, 0, "Broken MoQ request parameter"); + } else { + subscriber_priority = bytes[offset]; + length = 1; + } params->subscriber_priority = subscriber_priority; params->subscriber_priority_set = TRUE; IMQUIC_LOG(IMQUIC_MOQ_LOG_HUGE, "[%s][MoQ] -- -- -- %"SCNu8"\n", imquic_get_connection_name(moq->conn), params->subscriber_priority); len = length; - } else if(moq->version >= IMQUIC_MOQ_VERSION_15 && type == IMQUIC_MOQ_REQUEST_PARAM_GROUP_ORDER) { - uint64_t group_order = imquic_read_varint(&bytes[offset], blen-offset, &length); - IMQUIC_MOQ_CHECK_ERR(length == 0 || group_order > IMQUIC_MOQ_ORDERING_DESCENDING, NULL, 0, 0, "Broken MoQ request parameter"); + } else if(type == IMQUIC_MOQ_REQUEST_PARAM_GROUP_ORDER) { + uint64_t group_order = 0; + if(moq->version <= IMQUIC_MOQ_VERSION_16) { + group_order = imquic_read_moqint(moq->version, &bytes[offset], blen-offset, &length); + IMQUIC_MOQ_CHECK_ERR(length == 0 || group_order > 255, NULL, 0, 0, "Broken MoQ request parameter"); + } else { + group_order = bytes[offset]; + length = 1; + } params->group_order = group_order; params->group_order_set = TRUE; IMQUIC_LOG(IMQUIC_MOQ_LOG_HUGE, "[%s][MoQ] -- -- -- %"SCNu64" (%s)\n", imquic_get_connection_name(moq->conn), group_order, imquic_moq_group_order_str(group_order)); len = length; - } else if(moq->version >= IMQUIC_MOQ_VERSION_15 && type == IMQUIC_MOQ_REQUEST_PARAM_SUBSCRIPTION_FILTER) { + } else if(type == IMQUIC_MOQ_REQUEST_PARAM_SUBSCRIPTION_FILTER) { uint8_t *tmp = &bytes[offset]; size_t toffset = 0, tlen = len; - params->subscription_filter.type = imquic_read_varint(&tmp[toffset], tlen-toffset, &length); + params->subscription_filter.type = imquic_read_moqint(moq->version, &tmp[toffset], tlen-toffset, &length); IMQUIC_MOQ_CHECK_ERR(length == 0, NULL, 0, 0, "Broken MoQ request parameter"); toffset += length; if(params->subscription_filter.type == IMQUIC_MOQ_FILTER_ABSOLUTE_START || params->subscription_filter.type == IMQUIC_MOQ_FILTER_ABSOLUTE_RANGE) { - params->subscription_filter.start_location.group = imquic_read_varint(&tmp[toffset], tlen-toffset, &length); + params->subscription_filter.start_location.group = imquic_read_moqint(moq->version, &tmp[toffset], tlen-toffset, &length); IMQUIC_MOQ_CHECK_ERR(length == 0, NULL, 0, 0, "Broken MoQ request parameter"); toffset += length; - params->subscription_filter.start_location.object = imquic_read_varint(&tmp[toffset], tlen-toffset, &length); + params->subscription_filter.start_location.object = imquic_read_moqint(moq->version, &tmp[toffset], tlen-toffset, &length); IMQUIC_MOQ_CHECK_ERR(length == 0, NULL, 0, 0, "Broken MoQ request parameter"); toffset += length; } if(params->subscription_filter.type == IMQUIC_MOQ_FILTER_ABSOLUTE_RANGE) { - params->subscription_filter.end_group = imquic_read_varint(&tmp[toffset], tlen-toffset, &length); + params->subscription_filter.end_group = imquic_read_moqint(moq->version, &tmp[toffset], tlen-toffset, &length); IMQUIC_MOQ_CHECK_ERR(length == 0, NULL, 0, 0, "Broken MoQ request parameter"); + /* The End group property is a delta, starting from v17, but + * we expose the full actual value to the application */ + if(moq->version >= IMQUIC_MOQ_VERSION_17) + params->subscription_filter.end_group += params->subscription_filter.start_location.group; } params->subscription_filter_set = TRUE; IMQUIC_LOG(IMQUIC_MOQ_LOG_HUGE, "[%s][MoQ] -- -- -- %d\n", imquic_get_connection_name(moq->conn), params->subscription_filter.type); - } else if(moq->version >= IMQUIC_MOQ_VERSION_15 && type == IMQUIC_MOQ_REQUEST_PARAM_EXPIRES) { - params->expires = imquic_read_varint(&bytes[offset], blen-offset, &length); + } else if(type == IMQUIC_MOQ_REQUEST_PARAM_EXPIRES) { + params->expires = imquic_read_moqint(moq->version, &bytes[offset], blen-offset, &length); IMQUIC_MOQ_CHECK_ERR(length == 0, NULL, 0, 0, "Broken MoQ request parameter"); params->expires_set = TRUE; IMQUIC_LOG(IMQUIC_MOQ_LOG_HUGE, "[%s][MoQ] -- -- -- %"SCNu64"\n", imquic_get_connection_name(moq->conn), params->expires); len = length; - } else if(moq->version >= IMQUIC_MOQ_VERSION_15 && type == IMQUIC_MOQ_REQUEST_PARAM_LARGEST_OBJECT) { - uint8_t *tmp = &bytes[offset]; - size_t toffset = 0, tlen = len; - params->largest_object.group = imquic_read_varint(&tmp[toffset], tlen-toffset, &length); - IMQUIC_MOQ_CHECK_ERR(length == 0, NULL, 0, 0, "Broken MoQ request parameter"); - toffset += length; - params->largest_object.object = imquic_read_varint(&tmp[toffset], tlen-toffset, &length); - IMQUIC_MOQ_CHECK_ERR(length == 0, NULL, 0, 0, "Broken MoQ request parameter"); + } else if(type == IMQUIC_MOQ_REQUEST_PARAM_LARGEST_OBJECT) { + if(moq->version <= IMQUIC_MOQ_VERSION_16) { + uint8_t *tmp = &bytes[offset]; + size_t toffset = 0, tlen = len; + params->largest_object.group = imquic_read_moqint(moq->version, &tmp[toffset], tlen-toffset, &length); + IMQUIC_MOQ_CHECK_ERR(length == 0, NULL, 0, 0, "Broken MoQ request parameter"); + toffset += length; + params->largest_object.object = imquic_read_moqint(moq->version, &tmp[toffset], tlen-toffset, &length); + IMQUIC_MOQ_CHECK_ERR(length == 0, NULL, 0, 0, "Broken MoQ request parameter"); + } else { + params->largest_object.group = imquic_read_moqint(moq->version, &bytes[offset], blen-offset, &length); + IMQUIC_MOQ_CHECK_ERR(length == 0, NULL, 0, 0, "Broken MoQ request parameter"); + len = length; + params->largest_object.object = imquic_read_moqint(moq->version, &bytes[offset+length], blen-offset-length, &length); + IMQUIC_MOQ_CHECK_ERR(length == 0, NULL, 0, 0, "Broken MoQ request parameter"); + len += length; + } params->largest_object_set = TRUE; IMQUIC_LOG(IMQUIC_MOQ_LOG_HUGE, "[%s][MoQ] -- -- -- %"SCNu64" / %"SCNu64"\n", imquic_get_connection_name(moq->conn), params->largest_object.group, params->largest_object.object); - } else if(moq->version >= IMQUIC_MOQ_VERSION_15 && type == IMQUIC_MOQ_REQUEST_PARAM_FORWARD) { - uint64_t forward = imquic_read_varint(&bytes[offset], blen-offset, &length); - IMQUIC_MOQ_CHECK_ERR(length == 0 || forward > 1, NULL, 0, 0, "Broken MoQ request parameter"); + } else if(type == IMQUIC_MOQ_REQUEST_PARAM_FORWARD) { + uint64_t forward = 0; + if(moq->version <= IMQUIC_MOQ_VERSION_16) { + forward = imquic_read_moqint(moq->version, &bytes[offset], blen-offset, &length); + IMQUIC_MOQ_CHECK_ERR(length == 0 || forward > 255, NULL, 0, 0, "Broken MoQ request parameter"); + } else { + forward = bytes[offset]; + length = 1; + } params->forward = (forward > 0); params->forward_set = TRUE; IMQUIC_LOG(IMQUIC_MOQ_LOG_HUGE, "[%s][MoQ] -- -- -- %"SCNu8"\n", imquic_get_connection_name(moq->conn), params->forward); len = length; - } else if(moq->version == IMQUIC_MOQ_VERSION_15 && type == IMQUIC_MOQ_REQUEST_PARAM_DYNAMIC_GROUPS) { - uint64_t dynamic_groups = imquic_read_varint(&bytes[offset], blen-offset, &length); - IMQUIC_MOQ_CHECK_ERR(length == 0 || dynamic_groups > 2, NULL, 0, 0, "Broken MoQ request parameter"); - params->dynamic_groups = (dynamic_groups > 0); - params->dynamic_groups_set = TRUE; - IMQUIC_LOG(IMQUIC_MOQ_LOG_HUGE, "[%s][MoQ] -- -- -- %"SCNu8"\n", - imquic_get_connection_name(moq->conn), params->dynamic_groups); - len = length; - } else if(moq->version >= IMQUIC_MOQ_VERSION_15 && type == IMQUIC_MOQ_REQUEST_PARAM_NEW_GROUP_REQUEST) { - params->new_group_request = imquic_read_varint(&bytes[offset], blen-offset, &length); + } else if(type == IMQUIC_MOQ_REQUEST_PARAM_NEW_GROUP_REQUEST) { + params->new_group_request = imquic_read_moqint(moq->version, &bytes[offset], blen-offset, &length); IMQUIC_MOQ_CHECK_ERR(length == 0, NULL, 0, 0, "Broken MoQ request parameter"); params->new_group_request_set = TRUE; IMQUIC_LOG(IMQUIC_MOQ_LOG_HUGE, "[%s][MoQ] -- -- -- %"SCNu64"\n", imquic_get_connection_name(moq->conn), params->new_group_request); len = length; } else { - IMQUIC_LOG(IMQUIC_LOG_WARN, "[%s][MoQ] Unsupported parameter %"SCNu64"\n", - imquic_get_connection_name(moq->conn), type); - if(type % 2 == 0) - len = length; + if(moq->version <= IMQUIC_MOQ_VERSION_16) { + IMQUIC_LOG(IMQUIC_LOG_WARN, "[%s][MoQ] Unsupported parameter %"SCNu64"\n", + imquic_get_connection_name(moq->conn), type); + if(type % 2 == 0) + len = length; + } else { + IMQUIC_LOG(IMQUIC_LOG_ERR, "[%s][MoQ] Unsupported parameter %"SCNu64"\n", + imquic_get_connection_name(moq->conn), type); + if(error) + *error = IMQUIC_MOQ_PROTOCOL_VIOLATION; + return 0; + } } offset += len; if(error) @@ -6659,7 +5581,8 @@ int imquic_moq_set_connection_auth(imquic_connection *conn, uint8_t *auth, size_ int imquic_moq_set_max_request_id(imquic_connection *conn, uint64_t max_request_id) { imquic_mutex_lock(&moq_mutex); imquic_moq_context *moq = g_hash_table_lookup(moq_sessions, conn); - if(moq == NULL || max_request_id == 0 || moq->local_max_request_id >= max_request_id) { + if(moq == NULL || moq->version >= IMQUIC_MOQ_VERSION_17 || + max_request_id == 0 || moq->local_max_request_id >= max_request_id) { imquic_mutex_unlock(&moq_mutex); return -1; } @@ -6705,65 +5628,64 @@ const char *imquic_moq_get_remote_implementation(imquic_connection *conn) { return implementation; } -/* Object extensions management */ -GList *imquic_moq_parse_object_extensions(imquic_moq_version version, uint8_t *extensions, size_t elen) { - if(extensions == NULL || elen == 0) +/* Properties management */ +GList *imquic_moq_parse_properties(imquic_moq_version version, uint8_t *properties, size_t prlen) { + if(properties == NULL || prlen == 0) return NULL; - GList *exts = NULL; + GList *props = NULL; size_t offset = 0; uint8_t length = 0; uint64_t last_id = 0; - /* Parse extensions */ - while(elen-offset > 0) { - uint64_t ext_type = imquic_read_varint(&extensions[offset], elen-offset, &length); - if(length == 0 || length >= elen-offset) { - IMQUIC_LOG(IMQUIC_LOG_ERR, "Broken object extensions\n"); - g_list_free_full(exts, (GDestroyNotify)imquic_moq_object_extension_free); + /* Parse properties */ + while(prlen-offset > 0) { + uint64_t prop_type = imquic_read_moqint(version, &properties[offset], prlen-offset, &length); + if(length == 0 || length >= prlen-offset) { + IMQUIC_LOG(IMQUIC_LOG_ERR, "Broken properties\n"); + g_list_free_full(props, (GDestroyNotify)imquic_moq_property_free); return 0; } - if(version >= IMQUIC_MOQ_VERSION_16) - ext_type += last_id; - last_id = ext_type; + prop_type += last_id; + last_id = prop_type; offset += length; - if(ext_type % 2 == 0) { + if(prop_type % 2 == 0) { /* Even types are followed by a numeric value */ - uint64_t ext_val = imquic_read_varint(&extensions[offset], elen-offset, &length); - if(length == 0 || length > elen-offset) { - IMQUIC_LOG(IMQUIC_LOG_ERR, "Broken object extensions\n"); - g_list_free_full(exts, (GDestroyNotify)imquic_moq_object_extension_free); + uint64_t prop_val = imquic_read_moqint(version, &properties[offset], prlen-offset, &length); + if(length == 0 || length > prlen-offset) { + IMQUIC_LOG(IMQUIC_LOG_ERR, "Broken properties\n"); + g_list_free_full(props, (GDestroyNotify)imquic_moq_property_free); return 0; } offset += length; - imquic_moq_object_extension *extension = g_malloc0(sizeof(imquic_moq_object_extension)); - extension->id = ext_type; - extension->value.number = ext_val; - exts = g_list_prepend(exts, extension); + imquic_moq_property *property = g_malloc0(sizeof(imquic_moq_property)); + property->id = prop_type; + property->value.number = prop_val; + props = g_list_prepend(props, property); } else { /* Odd typed are followed by a length and a value */ - uint64_t ext_len = imquic_read_varint(&extensions[offset], elen-offset, &length); - if(length == 0 || length >= elen-offset || ext_len >= elen-offset) { - IMQUIC_LOG(IMQUIC_LOG_ERR, "Broken object extensions\n"); - g_list_free_full(exts, (GDestroyNotify)imquic_moq_object_extension_free); + uint64_t prop_len = imquic_read_moqint(version, &properties[offset], prlen-offset, &length); + if(length == 0 || length >= prlen-offset || prop_len >= prlen-offset) { + IMQUIC_LOG(IMQUIC_LOG_ERR, "Broken properties\n"); + g_list_free_full(props, (GDestroyNotify)imquic_moq_property_free); return 0; } /* TODO A length larger than UINT16_MAX should be a protocol violation error */ - //~ IMQUIC_MOQ_CHECK_ERR(ext_len > UINT16_MAX, error, IMQUIC_MOQ_PROTOCOL_VIOLATION, 0, "Invalid Key-Value-Pair length"); + //~ IMQUIC_MOQ_CHECK_ERR(prop_len > UINT16_MAX, error, IMQUIC_MOQ_PROTOCOL_VIOLATION, 0, "Invalid Key-Value-Pair length"); offset += length; - imquic_moq_object_extension *extension = g_malloc0(sizeof(imquic_moq_object_extension)); - extension->id = ext_type; - if(ext_len > 0) { - extension->value.data.length = ext_len; - extension->value.data.buffer = g_malloc(ext_len); - memcpy(extension->value.data.buffer, &extensions[offset], ext_len); + imquic_moq_property *property = g_malloc0(sizeof(imquic_moq_property)); + property->id = prop_type; + if(prop_len > 0) { + property->value.data.length = prop_len; + property->value.data.buffer = g_malloc(prop_len); + memcpy(property->value.data.buffer, &properties[offset], prop_len); } - exts = g_list_prepend(exts, extension); - offset += ext_len; + props = g_list_prepend(props, property); + offset += prop_len; } } - return g_list_reverse(exts); + return g_list_reverse(props); } -static int imquic_moq_extension_type_sort(imquic_moq_object_extension *a, imquic_moq_object_extension *b) { +static int imquic_moq_property_type_sort(imquic_moq_property *a, imquic_moq_property *b) { if(!a && !b) return 0; else if(!b || a->id < b->id) @@ -6773,45 +5695,42 @@ static int imquic_moq_extension_type_sort(imquic_moq_object_extension *a, imquic return 0; } -size_t imquic_moq_build_object_extensions(imquic_moq_version version, GList *extensions, uint8_t *bytes, size_t blen) { - if(extensions == NULL || bytes == NULL || blen == 0) +size_t imquic_moq_build_properties(imquic_moq_version version, GList *properties, uint8_t *bytes, size_t blen) { + if(properties == NULL || bytes == NULL || blen == 0) return 0; size_t offset = 0; - /* Starting from v16, extensions are encoded with the type delta-encoded, + /* Starting from v16, properties are encoded with the type delta-encoded, * which means we need to sort them all in increasing type order */ - GList *ordered = (version >= IMQUIC_MOQ_VERSION_16) ? - g_list_sort(g_list_copy(extensions), (GCompareFunc)imquic_moq_extension_type_sort) : extensions; + GList *ordered = g_list_sort(g_list_copy(properties), (GCompareFunc)imquic_moq_property_type_sort); GList *temp = ordered; uint64_t last_id = 0; while(temp) { - imquic_moq_object_extension *ext = (imquic_moq_object_extension *)temp->data; - offset += imquic_write_varint((version >= IMQUIC_MOQ_VERSION_16 ? (ext->id - last_id) : ext->id), - &bytes[offset], blen-offset); - last_id = ext->id; - if(ext->id % 2 == 0) { - offset += imquic_write_varint(ext->value.number, &bytes[offset], blen-offset); + imquic_moq_property *prop = (imquic_moq_property *)temp->data; + offset += imquic_write_moqint(version, (prop->id - last_id), &bytes[offset], blen-offset); + last_id = prop->id; + if(prop->id % 2 == 0) { + offset += imquic_write_moqint(version, prop->value.number, &bytes[offset], blen-offset); } else { - offset += imquic_write_varint(ext->value.data.length, &bytes[offset], blen-offset); - if(ext->value.data.length > 0) { - memcpy(&bytes[offset], ext->value.data.buffer, ext->value.data.length); - offset += ext->value.data.length; + offset += imquic_write_moqint(version, prop->value.data.length, &bytes[offset], blen-offset); + if(prop->value.data.length > 0) { + memcpy(&bytes[offset], prop->value.data.buffer, prop->value.data.length); + offset += prop->value.data.length; } } temp = temp->next; } - if(ordered != extensions) - g_list_free(ordered); + g_list_free(ordered); return offset; } /* Auth token management */ -int imquic_moq_parse_auth_token(uint8_t *bytes, size_t blen, imquic_moq_auth_token *token) { +int imquic_moq_parse_auth_token(imquic_moq_version version, uint8_t *bytes, size_t blen, imquic_moq_auth_token *token) { if(bytes == NULL || blen == 0 || token == NULL) return -1; memset(token, 0, sizeof(*token)); size_t offset = 0; uint8_t length = 0; - uint64_t alias_type = imquic_read_varint(&bytes[offset], blen-offset, &length); + uint64_t alias_type = imquic_read_moqint(version, &bytes[offset], blen-offset, &length); IMQUIC_MOQ_CHECK_ERR(length == 0 || length > blen-offset, NULL, 0, -1, "Broken auth token"); offset += length; if(alias_type != IMQUIC_MOQ_AUTH_TOKEN_DELETE && alias_type != IMQUIC_MOQ_AUTH_TOKEN_REGISTER && @@ -6821,14 +5740,14 @@ int imquic_moq_parse_auth_token(uint8_t *bytes, size_t blen, imquic_moq_auth_tok } token->alias_type = alias_type; if(alias_type != IMQUIC_MOQ_AUTH_TOKEN_USE_VALUE) { - uint64_t token_alias = imquic_read_varint(&bytes[offset], blen-offset, &length); + uint64_t token_alias = imquic_read_moqint(version, &bytes[offset], blen-offset, &length); IMQUIC_MOQ_CHECK_ERR(length == 0 || length > blen-offset, NULL, 0, -1, "Broken auth token"); offset += length; token->token_alias_set = TRUE; token->token_alias = token_alias; } if(alias_type == IMQUIC_MOQ_AUTH_TOKEN_REGISTER || alias_type == IMQUIC_MOQ_AUTH_TOKEN_USE_VALUE) { - uint64_t token_type = imquic_read_varint(&bytes[offset], blen-offset, &length); + uint64_t token_type = imquic_read_moqint(version, &bytes[offset], blen-offset, &length); IMQUIC_MOQ_CHECK_ERR(length == 0 || length > blen-offset, NULL, 0, -1, "Broken auth token"); offset += length; token->token_type_set = TRUE; @@ -6839,7 +5758,7 @@ int imquic_moq_parse_auth_token(uint8_t *bytes, size_t blen, imquic_moq_auth_tok return 0; } -size_t imquic_moq_build_auth_token(imquic_moq_auth_token *token, uint8_t *bytes, size_t blen) { +size_t imquic_moq_build_auth_token(imquic_moq_version version, imquic_moq_auth_token *token, uint8_t *bytes, size_t blen) { if(token == NULL || bytes == NULL || blen == 0) return 0; if(token->alias_type != IMQUIC_MOQ_AUTH_TOKEN_DELETE && token->alias_type != IMQUIC_MOQ_AUTH_TOKEN_REGISTER && @@ -6847,20 +5766,20 @@ size_t imquic_moq_build_auth_token(imquic_moq_auth_token *token, uint8_t *bytes, IMQUIC_LOG(IMQUIC_LOG_ERR, "Invalid alias type %d\n", token->alias_type); return 0; } - size_t offset = imquic_write_varint(token->alias_type, bytes, blen); + size_t offset = imquic_write_moqint(version, token->alias_type, bytes, blen); if(token->alias_type != IMQUIC_MOQ_AUTH_TOKEN_USE_VALUE) { if(!token->token_alias_set) { IMQUIC_LOG(IMQUIC_LOG_ERR, "Token alias is required when using %s\n", imquic_moq_auth_token_alias_type_str(token->alias_type)); return 0; } - offset += imquic_write_varint(token->token_alias, &bytes[offset], blen-offset); + offset += imquic_write_moqint(version, token->token_alias, &bytes[offset], blen-offset); } if(token->alias_type == IMQUIC_MOQ_AUTH_TOKEN_REGISTER || token->alias_type == IMQUIC_MOQ_AUTH_TOKEN_USE_VALUE) { if(!token->token_type_set) { IMQUIC_LOG(IMQUIC_LOG_ERR, "Token type is required when using %s\n", imquic_moq_auth_token_alias_type_str(token->alias_type)); return 0; } - offset += imquic_write_varint(token->token_type, &bytes[offset], blen-offset); + offset += imquic_write_moqint(version, token->token_type, &bytes[offset], blen-offset); if(token->token_value.buffer && token->token_value.length > 0) { memcpy(&bytes[offset], token->token_value.buffer, token->token_value.length); offset += token->token_value.length; @@ -6870,8 +5789,8 @@ size_t imquic_moq_build_auth_token(imquic_moq_auth_token *token, uint8_t *bytes, } /* Namespaces and subscriptions */ -int imquic_moq_publish_namespace(imquic_connection *conn, uint64_t request_id, imquic_moq_namespace *tns, - imquic_moq_request_parameters *parameters) { +int imquic_moq_publish_namespace(imquic_connection *conn, uint64_t request_id, uint64_t required_id_delta, + imquic_moq_namespace *tns, imquic_moq_request_parameters *parameters) { imquic_mutex_lock(&moq_mutex); imquic_moq_context *moq = g_hash_table_lookup(moq_sessions, conn); if(moq == NULL || tns == NULL || tns->buffer == 0 || tns->length == 0) { @@ -6889,18 +5808,30 @@ int imquic_moq_publish_namespace(imquic_connection *conn, uint64_t request_id, i moq->next_request_id = request_id + IMQUIC_MOQ_REQUEST_ID_INCREMENT; imquic_refcount_increase(&moq->ref); imquic_mutex_unlock(&moq_mutex); - /* TODO Check if this namespace exists and was publish_namespaced here */ - if(moq->version >= IMQUIC_MOQ_VERSION_15) { - /* Map this request ID to this message type, so that we can trigger - * the right application callbac if/when we get a response later on */ + /* Map this request ID to this message type, so that we can trigger + * the right application callbac if/when we get a response later on */ + imquic_mutex_lock(&moq->mutex); + g_hash_table_insert(moq->requests, imquic_dup_uint64(request_id), GUINT_TO_POINTER(IMQUIC_MOQ_PUBLISH_NAMESPACE)); + imquic_mutex_unlock(&moq->mutex); + /* Starting from v17, requests on a dedicated bidirectional STREAM */ + imquic_moq_stream *moq_stream = NULL; + if(moq->version >= IMQUIC_MOQ_VERSION_17) { + moq_stream = imquic_moq_stream_create(); + imquic_connection_new_stream_id(moq->conn, TRUE, &moq_stream->stream_id); + moq_stream->request_type = IMQUIC_MOQ_PUBLISH_NAMESPACE; + moq_stream->request_id = request_id; + moq_stream->request_sender = TRUE; + moq_stream->request_state = IMQUIC_MOQ_REQUEST_STATE_SENT; imquic_mutex_lock(&moq->mutex); - g_hash_table_insert(moq->requests, imquic_dup_uint64(request_id), GUINT_TO_POINTER(IMQUIC_MOQ_PUBLISH_NAMESPACE)); + g_hash_table_insert(moq->streams, imquic_dup_uint64(moq_stream->stream_id), moq_stream); + g_hash_table_insert(moq->streams_by_reqid, imquic_dup_uint64(request_id), moq_stream); imquic_mutex_unlock(&moq->mutex); } uint8_t buffer[200]; size_t blen = sizeof(buffer); - size_t ann_len = imquic_moq_add_publish_namespace(moq, buffer, blen, request_id, tns, parameters); - imquic_connection_send_on_stream(conn, moq->control_stream_id, + size_t ann_len = imquic_moq_add_publish_namespace(moq, moq_stream, buffer, blen, request_id, required_id_delta, tns, parameters); + imquic_connection_send_on_stream(conn, + moq_stream ? moq_stream->stream_id : moq->control_stream_id, buffer, ann_len, FALSE); /* Done */ imquic_refcount_decrease(&moq->ref); @@ -6916,19 +5847,30 @@ int imquic_moq_accept_publish_namespace(imquic_connection *conn, uint64_t reques imquic_mutex_unlock(&moq_mutex); return -1; } - imquic_refcount_increase(&moq->ref); - imquic_mutex_unlock(&moq_mutex); - /* TODO Check if the request ID exists */ - /* TODO Check if this namespace exists and was publish_namespaced here */ + imquic_refcount_increase(&moq->ref); + imquic_mutex_unlock(&moq_mutex); + /* Starting from v17, requests go on a dedicated bidirectional + * STREAM, and the same applies to the REQUEST_OK/ERROR responses */ + imquic_moq_stream *moq_stream = NULL; + if(moq->version >= IMQUIC_MOQ_VERSION_17) { + imquic_mutex_lock(&moq->mutex); + moq_stream = g_hash_table_lookup(moq->streams_by_reqid, &request_id); + if(moq_stream == NULL || moq_stream->request_type != IMQUIC_MOQ_PUBLISH_NAMESPACE || moq_stream->request_sender || + moq_stream->request_state != IMQUIC_MOQ_REQUEST_STATE_SENT) { + IMQUIC_LOG(IMQUIC_LOG_ERR, "[%s][MoQ] Invalid request/state (%s)\n", + imquic_get_connection_name(conn), moq_stream ? imquic_media_stream_request_state_str(moq_stream->request_state) : "No stream"); + imquic_mutex_unlock(&moq->mutex); + imquic_refcount_decrease(&moq->ref); + return -1; + } + moq_stream->request_state = IMQUIC_MOQ_REQUEST_STATE_OK; + imquic_mutex_unlock(&moq->mutex); + } uint8_t buffer[200]; size_t blen = sizeof(buffer); - size_t ann_len = 0; - if(moq->version <= IMQUIC_MOQ_VERSION_14) { - ann_len = imquic_moq_add_publish_namespace_ok(moq, buffer, blen, request_id); - } else { - ann_len = imquic_moq_add_request_ok(moq, NULL, buffer, blen, request_id, parameters); - } - imquic_connection_send_on_stream(conn, moq->control_stream_id, + size_t ann_len = imquic_moq_add_request_ok(moq, moq_stream, buffer, blen, request_id, parameters); + imquic_connection_send_on_stream(conn, + moq_stream ? moq_stream->stream_id : moq->control_stream_id, buffer, ann_len, FALSE); /* Done */ imquic_refcount_decrease(&moq->ref); @@ -6947,27 +5889,43 @@ int imquic_moq_reject_publish_namespace(imquic_connection *conn, uint64_t reques } imquic_refcount_increase(&moq->ref); imquic_mutex_unlock(&moq_mutex); - /* TODO Check if the request ID exists */ - /* TODO Check if this namespace exists and was publish_namespaced here */ + /* Starting from v17, requests go on a dedicated bidirectional + * STREAM, and the same applies to the REQUEST_OK/ERROR responses */ + imquic_moq_stream *moq_stream = NULL; + if(moq->version >= IMQUIC_MOQ_VERSION_17) { + imquic_mutex_lock(&moq->mutex); + moq_stream = g_hash_table_lookup(moq->streams_by_reqid, &request_id); + if(moq_stream == NULL || moq_stream->request_type != IMQUIC_MOQ_PUBLISH_NAMESPACE || moq_stream->request_sender || + moq_stream->request_state != IMQUIC_MOQ_REQUEST_STATE_SENT) { + IMQUIC_LOG(IMQUIC_LOG_ERR, "[%s][MoQ] Invalid request/state (%s)\n", + imquic_get_connection_name(conn), moq_stream ? imquic_media_stream_request_state_str(moq_stream->request_state) : "No stream"); + imquic_mutex_unlock(&moq->mutex); + imquic_refcount_decrease(&moq->ref); + return -1; + } + moq_stream->request_state = IMQUIC_MOQ_REQUEST_STATE_ERROR; + imquic_mutex_unlock(&moq->mutex); + } uint8_t buffer[200]; size_t blen = sizeof(buffer); - size_t ann_len = 0; - if(moq->version <= IMQUIC_MOQ_VERSION_14) { - ann_len = imquic_moq_add_publish_namespace_error(moq, buffer, blen, request_id, error_code, reason); - } else { - ann_len = imquic_moq_add_request_error(moq, NULL, buffer, blen, request_id, error_code, reason, retry_interval); + size_t ann_len = imquic_moq_add_request_error(moq, moq_stream, buffer, blen, request_id, error_code, reason, retry_interval); + imquic_connection_send_on_stream(conn, + moq_stream ? moq_stream->stream_id : moq->control_stream_id, + buffer, ann_len, moq_stream ? TRUE : FALSE); + if(moq_stream != NULL) { + imquic_mutex_lock(&moq->mutex); + g_hash_table_remove(moq->streams, &moq_stream->stream_id); + imquic_mutex_unlock(&moq->mutex); } - imquic_connection_send_on_stream(conn, moq->control_stream_id, - buffer, ann_len, FALSE); /* Done */ imquic_refcount_decrease(&moq->ref); return 0; } -int imquic_moq_publish_namespace_done(imquic_connection *conn, imquic_moq_namespace *tns) { +int imquic_moq_publish_namespace_done(imquic_connection *conn, uint64_t request_id) { imquic_mutex_lock(&moq_mutex); imquic_moq_context *moq = g_hash_table_lookup(moq_sessions, conn); - if(moq == NULL || tns == NULL || tns->buffer == 0 || tns->length == 0) { + if(moq == NULL) { IMQUIC_LOG(IMQUIC_LOG_ERR, "[%s][MoQ] Invalid arguments\n", imquic_get_connection_name(conn)); imquic_mutex_unlock(&moq_mutex); @@ -6975,10 +5933,32 @@ int imquic_moq_publish_namespace_done(imquic_connection *conn, imquic_moq_namesp } imquic_refcount_increase(&moq->ref); imquic_mutex_unlock(&moq_mutex); - /* TODO Check if this namespace exists and was publish_namespaced here */ + /* Check if we have a STREAM to close */ + if(moq->version >= IMQUIC_MOQ_VERSION_17) { + /* On newer versions of MoQ, requests uses a dedicated + * bidirectional STREAM, so we simply close the STREAM */ + imquic_mutex_lock(&moq->mutex); + imquic_moq_stream *moq_stream = g_hash_table_lookup(moq->streams_by_reqid, &request_id); + if(moq_stream == NULL || moq_stream->request_type != IMQUIC_MOQ_PUBLISH_NAMESPACE || !moq_stream->request_sender || + (moq_stream->request_state != IMQUIC_MOQ_REQUEST_STATE_OK && moq_stream->request_state != IMQUIC_MOQ_REQUEST_STATE_UPDATE_SENT)) { + IMQUIC_LOG(IMQUIC_LOG_ERR, "[%s][MoQ] Invalid request/state (%s)\n", + imquic_get_connection_name(conn), moq_stream ? imquic_media_stream_request_state_str(moq_stream->request_state) : "No stream"); + imquic_mutex_unlock(&moq->mutex); + imquic_refcount_decrease(&moq->ref); + return -1; + } + /* Reset the STREAM */ + imquic_connection_reset_stream(moq->conn, moq_stream->stream_id, IMQUIC_MOQ_RESET_CANCELLED); + g_hash_table_remove(moq->streams_by_reqid, &moq_stream->request_id); + g_hash_table_remove(moq->streams, &moq_stream->stream_id); + imquic_mutex_unlock(&moq->mutex); + imquic_refcount_decrease(&moq->ref); + return 0; + } + /* If we're here,we're sending the legacy PUBLISH_NAMESPACE_DONE */ uint8_t buffer[200]; size_t blen = sizeof(buffer); - size_t ann_len = imquic_moq_add_publish_namespace_done(moq, buffer, blen, tns); + size_t ann_len = imquic_moq_add_publish_namespace_done(moq, buffer, blen, request_id); imquic_connection_send_on_stream(conn, moq->control_stream_id, buffer, ann_len, FALSE); /* Done */ @@ -6986,8 +5966,8 @@ int imquic_moq_publish_namespace_done(imquic_connection *conn, imquic_moq_namesp return 0; } -int imquic_moq_publish(imquic_connection *conn, uint64_t request_id, imquic_moq_namespace *tns, imquic_moq_name *tn, - uint64_t track_alias, imquic_moq_request_parameters *parameters, GList *track_extensions) { +int imquic_moq_publish(imquic_connection *conn, uint64_t request_id, uint64_t required_id_delta, imquic_moq_namespace *tns, imquic_moq_name *tn, + uint64_t track_alias, imquic_moq_request_parameters *parameters, GList *track_properties) { imquic_mutex_lock(&moq_mutex); imquic_moq_context *moq = g_hash_table_lookup(moq_sessions, conn); if(moq == NULL || tns == NULL || tns->buffer == 0 || tns->length == 0 || @@ -6997,23 +5977,6 @@ int imquic_moq_publish(imquic_connection *conn, uint64_t request_id, imquic_moq_ imquic_mutex_unlock(&moq_mutex); return -1; } - if(moq->version < IMQUIC_MOQ_VERSION_12) { - IMQUIC_LOG(IMQUIC_LOG_ERR, "[%s][MoQ] Publishing not supported on a connection using %s\n", - imquic_get_connection_name(conn), imquic_moq_version_str(moq->version)); - imquic_refcount_decrease(&moq->ref); - return -1; - } - if(moq->version <= IMQUIC_MOQ_VERSION_14 && parameters == NULL) { - IMQUIC_LOG(IMQUIC_LOG_ERR, "[%s][MoQ] Invalid arguments (missing mandatory parameters)\n", - imquic_get_connection_name(conn)); - imquic_mutex_unlock(&moq_mutex); - return -1; - } - if(moq->version <= IMQUIC_MOQ_VERSION_14 && !parameters->group_order_set) { - /* Force some defaults */ - parameters->group_order_set = TRUE; - parameters->group_order = IMQUIC_MOQ_ORDERING_ASCENDING; - } /* Make sure we can send this */ if(!moq_is_request_id_valid(moq, request_id, TRUE)) { IMQUIC_LOG(IMQUIC_LOG_ERR, "[%s][MoQ] Invalid Request ID\n", imquic_get_connection_name(conn)); @@ -7029,20 +5992,32 @@ int imquic_moq_publish(imquic_connection *conn, uint64_t request_id, imquic_moq_ g_hash_table_insert(moq->subscriptions_by_id, imquic_dup_uint64(request_id), moq_sub); g_hash_table_insert(moq->subscriptions, imquic_dup_uint64(track_alias), moq_sub); imquic_mutex_unlock(&moq->mutex); - /* Send the request */ - if(moq->version >= IMQUIC_MOQ_VERSION_15) { - /* Map this request ID to this message type, so that we can trigger - * the right application callbac if/when we get a response later on */ + /* Map this request ID to this message type, so that we can trigger + * the right application callbac if/when we get a response later on */ + imquic_mutex_lock(&moq->mutex); + g_hash_table_insert(moq->requests, imquic_dup_uint64(request_id), GUINT_TO_POINTER(IMQUIC_MOQ_PUBLISH)); + imquic_mutex_unlock(&moq->mutex); + /* Starting from v17, requests on a dedicated bidirectional STREAM */ + imquic_moq_stream *moq_stream = NULL; + if(moq->version >= IMQUIC_MOQ_VERSION_17) { + moq_stream = imquic_moq_stream_create(); + imquic_connection_new_stream_id(moq->conn, TRUE, &moq_stream->stream_id); + moq_stream->request_type = IMQUIC_MOQ_PUBLISH; + moq_stream->request_id = request_id; + moq_stream->request_sender = TRUE; + moq_stream->request_state = IMQUIC_MOQ_REQUEST_STATE_SENT; imquic_mutex_lock(&moq->mutex); - g_hash_table_insert(moq->requests, imquic_dup_uint64(request_id), GUINT_TO_POINTER(IMQUIC_MOQ_PUBLISH)); + g_hash_table_insert(moq->streams, imquic_dup_uint64(moq_stream->stream_id), moq_stream); + g_hash_table_insert(moq->streams_by_reqid, imquic_dup_uint64(request_id), moq_stream); imquic_mutex_unlock(&moq->mutex); } uint8_t buffer[200]; size_t blen = sizeof(buffer); size_t sb_len = 0; - sb_len = imquic_moq_add_publish(moq, buffer, blen, - request_id, tns, tn, track_alias, parameters, track_extensions); - imquic_connection_send_on_stream(conn, moq->control_stream_id, + sb_len = imquic_moq_add_publish(moq, moq_stream, buffer, blen, + request_id, required_id_delta, tns, tn, track_alias, parameters, track_properties); + imquic_connection_send_on_stream(conn, + moq_stream ? moq_stream->stream_id : moq->control_stream_id, buffer, sb_len, FALSE); /* Done */ imquic_refcount_decrease(&moq->ref); @@ -7058,40 +6033,8 @@ int imquic_moq_accept_publish(imquic_connection *conn, uint64_t request_id, imqu imquic_mutex_unlock(&moq_mutex); return -1; } - if(moq->version <= IMQUIC_MOQ_VERSION_14 && parameters == NULL) { - IMQUIC_LOG(IMQUIC_LOG_ERR, "[%s][MoQ] Invalid arguments (missing mandatory parameters)\n", - imquic_get_connection_name(conn)); - imquic_mutex_unlock(&moq_mutex); - return -1; - } - if(moq->version <= IMQUIC_MOQ_VERSION_14 && !parameters->forward_set) { - /* Force some defaults */ - parameters->forward_set = TRUE; - parameters->forward = TRUE; - } - if(moq->version <= IMQUIC_MOQ_VERSION_14 && !parameters->subscriber_priority_set) { - /* Force some defaults */ - parameters->subscriber_priority_set = TRUE; - parameters->subscriber_priority = 128; - } - if(moq->version <= IMQUIC_MOQ_VERSION_14 && !parameters->group_order_set) { - /* Force some defaults */ - parameters->group_order_set = TRUE; - parameters->group_order = IMQUIC_MOQ_ORDERING_ASCENDING; - } - if(moq->version <= IMQUIC_MOQ_VERSION_14 && !parameters->subscription_filter_set) { - /* Force some defaults */ - parameters->subscription_filter_set = TRUE; - parameters->subscription_filter.type = IMQUIC_MOQ_FILTER_LARGEST_OBJECT; - } imquic_refcount_increase(&moq->ref); imquic_mutex_unlock(&moq_mutex); - if(moq->version < IMQUIC_MOQ_VERSION_12) { - IMQUIC_LOG(IMQUIC_LOG_ERR, "[%s][MoQ] Publishing not supported on a connection using %s\n", - imquic_get_connection_name(conn), imquic_moq_version_str(moq->version)); - imquic_refcount_decrease(&moq->ref); - return -1; - } if(parameters && parameters->subscription_filter_set && parameters->subscription_filter.type == IMQUIC_MOQ_FILTER_ABSOLUTE_RANGE && parameters->subscription_filter.end_group > 0 && parameters->subscription_filter.start_location.group > parameters->subscription_filter.end_group) { IMQUIC_LOG(IMQUIC_LOG_ERR, "[%s][MoQ] End group is lower than start location group (%"SCNu64" < %"SCNu64")\n", @@ -7101,12 +6044,29 @@ int imquic_moq_accept_publish(imquic_connection *conn, uint64_t request_id, imqu imquic_refcount_decrease(&moq->ref); return -1; } - /* TODO Check if we were subscribed */ + /* Starting from v17, requests go on a dedicated bidirectional + * STREAM, and the same applies to the PUBLISH_OK responses */ + imquic_moq_stream *moq_stream = NULL; + if(moq->version >= IMQUIC_MOQ_VERSION_17) { + imquic_mutex_lock(&moq->mutex); + moq_stream = g_hash_table_lookup(moq->streams_by_reqid, &request_id); + if(moq_stream == NULL || moq_stream->request_type != IMQUIC_MOQ_PUBLISH || moq_stream->request_sender || + moq_stream->request_state != IMQUIC_MOQ_REQUEST_STATE_SENT) { + IMQUIC_LOG(IMQUIC_LOG_ERR, "[%s][MoQ] Invalid request/state (%s)\n", + imquic_get_connection_name(conn), moq_stream ? imquic_media_stream_request_state_str(moq_stream->request_state) : "No stream"); + imquic_mutex_unlock(&moq->mutex); + imquic_refcount_decrease(&moq->ref); + return -1; + } + moq_stream->request_state = IMQUIC_MOQ_REQUEST_STATE_OK; + imquic_mutex_unlock(&moq->mutex); + } uint8_t buffer[200]; size_t blen = sizeof(buffer); size_t sb_len = 0; - sb_len = imquic_moq_add_publish_ok(moq, buffer, blen, request_id, parameters); - imquic_connection_send_on_stream(conn, moq->control_stream_id, + sb_len = imquic_moq_add_publish_ok(moq, moq_stream, buffer, blen, request_id, parameters); + imquic_connection_send_on_stream(conn, + moq_stream ? moq_stream->stream_id : moq->control_stream_id, buffer, sb_len, FALSE); /* Done */ imquic_refcount_decrease(&moq->ref); @@ -7125,29 +6085,41 @@ int imquic_moq_reject_publish(imquic_connection *conn, uint64_t request_id, } imquic_refcount_increase(&moq->ref); imquic_mutex_unlock(&moq_mutex); - if(moq->version < IMQUIC_MOQ_VERSION_12) { - IMQUIC_LOG(IMQUIC_LOG_ERR, "[%s][MoQ] Publishing not supported on a connection using %s\n", - imquic_get_connection_name(conn), imquic_moq_version_str(moq->version)); - imquic_refcount_decrease(&moq->ref); - return -1; + /* Starting from v17, requests go on a dedicated bidirectional + * STREAM, and the same applies to the REQUEST_ERROR responses */ + imquic_moq_stream *moq_stream = NULL; + if(moq->version >= IMQUIC_MOQ_VERSION_17) { + imquic_mutex_lock(&moq->mutex); + moq_stream = g_hash_table_lookup(moq->streams_by_reqid, &request_id); + if(moq_stream == NULL || moq_stream->request_type != IMQUIC_MOQ_PUBLISH_NAMESPACE || moq_stream->request_sender || + moq_stream->request_state != IMQUIC_MOQ_REQUEST_STATE_SENT) { + IMQUIC_LOG(IMQUIC_LOG_ERR, "[%s][MoQ] Invalid request/state (%s)\n", + imquic_get_connection_name(conn), moq_stream ? imquic_media_stream_request_state_str(moq_stream->request_state) : "No stream"); + imquic_mutex_unlock(&moq->mutex); + imquic_refcount_decrease(&moq->ref); + return -1; + } + moq_stream->request_state = IMQUIC_MOQ_REQUEST_STATE_ERROR; + imquic_mutex_unlock(&moq->mutex); } - /* TODO Check if we were subscribed */ uint8_t buffer[200]; size_t blen = sizeof(buffer); - size_t sb_len = 0; - if(moq->version <= IMQUIC_MOQ_VERSION_14) { - sb_len = imquic_moq_add_publish_error(moq, buffer, blen, request_id, error_code, reason); - } else { - sb_len = imquic_moq_add_request_error(moq, NULL, buffer, blen, request_id, error_code, reason, retry_interval); + size_t sb_len = imquic_moq_add_request_error(moq, moq_stream, buffer, blen, request_id, error_code, reason, retry_interval); + imquic_connection_send_on_stream(conn, + moq_stream ? moq_stream->stream_id : moq->control_stream_id, + buffer, sb_len, moq_stream ? TRUE : FALSE); + if(moq_stream != NULL) { + imquic_mutex_lock(&moq->mutex); + g_hash_table_remove(moq->streams_by_reqid, &moq_stream->request_id); + g_hash_table_remove(moq->streams, &moq_stream->stream_id); + imquic_mutex_unlock(&moq->mutex); } - imquic_connection_send_on_stream(conn, moq->control_stream_id, - buffer, sb_len, FALSE); /* Done */ imquic_refcount_decrease(&moq->ref); return 0; } -int imquic_moq_subscribe(imquic_connection *conn, uint64_t request_id, uint64_t track_alias, +int imquic_moq_subscribe(imquic_connection *conn, uint64_t request_id, uint64_t required_id_delta, imquic_moq_namespace *tns, imquic_moq_name *tn, imquic_moq_request_parameters *parameters) { imquic_mutex_lock(&moq_mutex); imquic_moq_context *moq = g_hash_table_lookup(moq_sessions, conn); @@ -7158,32 +6130,6 @@ int imquic_moq_subscribe(imquic_connection *conn, uint64_t request_id, uint64_t imquic_mutex_unlock(&moq_mutex); return -1; } - if(moq->version <= IMQUIC_MOQ_VERSION_14 && parameters == NULL) { - IMQUIC_LOG(IMQUIC_LOG_ERR, "[%s][MoQ] Invalid arguments (missing mandatory parameters)\n", - imquic_get_connection_name(conn)); - imquic_mutex_unlock(&moq_mutex); - return -1; - } - if(moq->version <= IMQUIC_MOQ_VERSION_14 && !parameters->forward_set) { - /* Force some defaults */ - parameters->forward_set = TRUE; - parameters->forward = TRUE; - } - if(moq->version <= IMQUIC_MOQ_VERSION_14 && !parameters->subscriber_priority_set) { - /* Force some defaults */ - parameters->subscriber_priority_set = TRUE; - parameters->subscriber_priority = 128; - } - if(moq->version <= IMQUIC_MOQ_VERSION_14 && !parameters->group_order_set) { - /* Force some defaults */ - parameters->group_order_set = TRUE; - parameters->group_order = IMQUIC_MOQ_ORDERING_ASCENDING; - } - if(moq->version <= IMQUIC_MOQ_VERSION_14 && !parameters->subscription_filter_set) { - /* Force some defaults */ - parameters->subscription_filter_set = TRUE; - parameters->subscription_filter.type = IMQUIC_MOQ_FILTER_LARGEST_OBJECT; - } if(parameters && parameters->subscription_filter_set && parameters->subscription_filter.type == IMQUIC_MOQ_FILTER_ABSOLUTE_RANGE && parameters->subscription_filter.end_group > 0 && parameters->subscription_filter.start_location.group > parameters->subscription_filter.end_group) { IMQUIC_LOG(IMQUIC_LOG_ERR, "[%s][MoQ] End group is lower than start location group (%"SCNu64" < %"SCNu64")\n", @@ -7202,20 +6148,32 @@ int imquic_moq_subscribe(imquic_connection *conn, uint64_t request_id, uint64_t moq->next_request_id = request_id + IMQUIC_MOQ_REQUEST_ID_INCREMENT; imquic_refcount_increase(&moq->ref); imquic_mutex_unlock(&moq_mutex); - /* Send the request */ - if(moq->version >= IMQUIC_MOQ_VERSION_15) { - /* Map this request ID to this message type, so that we can trigger - * the right application callbac if/when we get a response later on */ + /* Map this request ID to this message type, so that we can trigger + * the right application callbac if/when we get a response later on */ + imquic_mutex_lock(&moq->mutex); + g_hash_table_insert(moq->requests, imquic_dup_uint64(request_id), GUINT_TO_POINTER(IMQUIC_MOQ_SUBSCRIBE)); + imquic_mutex_unlock(&moq->mutex); + /* Starting from v17, requests on a dedicated bidirectional STREAM */ + imquic_moq_stream *moq_stream = NULL; + if(moq->version >= IMQUIC_MOQ_VERSION_17) { + moq_stream = imquic_moq_stream_create(); + imquic_connection_new_stream_id(moq->conn, TRUE, &moq_stream->stream_id); + moq_stream->request_type = IMQUIC_MOQ_SUBSCRIBE; + moq_stream->request_id = request_id; + moq_stream->request_sender = TRUE; + moq_stream->request_state = IMQUIC_MOQ_REQUEST_STATE_SENT; imquic_mutex_lock(&moq->mutex); - g_hash_table_insert(moq->requests, imquic_dup_uint64(request_id), GUINT_TO_POINTER(IMQUIC_MOQ_SUBSCRIBE)); + g_hash_table_insert(moq->streams, imquic_dup_uint64(moq_stream->stream_id), moq_stream); + g_hash_table_insert(moq->streams_by_reqid, imquic_dup_uint64(request_id), moq_stream); imquic_mutex_unlock(&moq->mutex); } uint8_t buffer[200]; size_t blen = sizeof(buffer); size_t sb_len = 0; - sb_len = imquic_moq_add_subscribe(moq, buffer, blen, - request_id, track_alias, tns, tn, parameters); - imquic_connection_send_on_stream(conn, moq->control_stream_id, + sb_len = imquic_moq_add_subscribe(moq, moq_stream, buffer, blen, + request_id, required_id_delta, tns, tn, parameters); + imquic_connection_send_on_stream(conn, + moq_stream ? moq_stream->stream_id : moq->control_stream_id, buffer, sb_len, FALSE); /* Done */ imquic_refcount_decrease(&moq->ref); @@ -7223,7 +6181,7 @@ int imquic_moq_subscribe(imquic_connection *conn, uint64_t request_id, uint64_t } int imquic_moq_accept_subscribe(imquic_connection *conn, uint64_t request_id, uint64_t track_alias, - imquic_moq_request_parameters *parameters, GList *track_extensions) { + imquic_moq_request_parameters *parameters, GList *track_properties) { imquic_mutex_lock(&moq_mutex); imquic_moq_context *moq = g_hash_table_lookup(moq_sessions, conn); if(moq == NULL) { @@ -7232,40 +6190,39 @@ int imquic_moq_accept_subscribe(imquic_connection *conn, uint64_t request_id, ui imquic_mutex_unlock(&moq_mutex); return -1; } - if(moq->version <= IMQUIC_MOQ_VERSION_14 && parameters == NULL) { - IMQUIC_LOG(IMQUIC_LOG_ERR, "[%s][MoQ] Invalid arguments (missing mandatory parameters)\n", - imquic_get_connection_name(conn)); - imquic_mutex_unlock(&moq_mutex); - return -1; - } - if(moq->version <= IMQUIC_MOQ_VERSION_14 && !parameters->expires_set) { - /* Force some defaults */ - parameters->expires_set = TRUE; - parameters->expires = 0; - } - if(moq->version <= IMQUIC_MOQ_VERSION_14 && !parameters->group_order_set) { - /* Force some defaults */ - parameters->group_order_set = TRUE; - parameters->group_order = IMQUIC_MOQ_ORDERING_ASCENDING; - } imquic_refcount_increase(&moq->ref); imquic_mutex_unlock(&moq_mutex); - /* TODO Check if we were subscribed */ - if(moq->version >= IMQUIC_MOQ_VERSION_12) { + /* Starting from v17, requests go on a dedicated bidirectional + * STREAM, and the same applies to the SUBSCRIBE_OK responses */ + imquic_moq_stream *moq_stream = NULL; + if(moq->version >= IMQUIC_MOQ_VERSION_17) { imquic_mutex_lock(&moq->mutex); - imquic_moq_subscription *moq_sub = g_hash_table_lookup(moq->subscriptions_by_id, &request_id); - if(moq_sub != NULL) { - /* Track this subscription */ - moq_sub->track_alias = track_alias; - g_hash_table_insert(moq->subscriptions, imquic_dup_uint64(track_alias), moq_sub); + moq_stream = g_hash_table_lookup(moq->streams_by_reqid, &request_id); + if(moq_stream == NULL || moq_stream->request_type != IMQUIC_MOQ_SUBSCRIBE || moq_stream->request_sender || + moq_stream->request_state != IMQUIC_MOQ_REQUEST_STATE_SENT) { + IMQUIC_LOG(IMQUIC_LOG_ERR, "[%s][MoQ] Invalid request/state (%s)\n", + imquic_get_connection_name(conn), moq_stream ? imquic_media_stream_request_state_str(moq_stream->request_state) : "No stream"); + imquic_mutex_unlock(&moq->mutex); + imquic_refcount_decrease(&moq->ref); + return -1; } + moq_stream->request_state = IMQUIC_MOQ_REQUEST_STATE_OK; imquic_mutex_unlock(&moq->mutex); } + imquic_mutex_lock(&moq->mutex); + imquic_moq_subscription *moq_sub = g_hash_table_lookup(moq->subscriptions_by_id, &request_id); + if(moq_sub != NULL) { + /* Track this subscription */ + moq_sub->track_alias = track_alias; + g_hash_table_insert(moq->subscriptions, imquic_dup_uint64(track_alias), moq_sub); + } + imquic_mutex_unlock(&moq->mutex); uint8_t buffer[200]; size_t blen = sizeof(buffer); - size_t sb_len = imquic_moq_add_subscribe_ok(moq, buffer, blen, - request_id, track_alias, parameters, track_extensions); - imquic_connection_send_on_stream(conn, moq->control_stream_id, + size_t sb_len = imquic_moq_add_subscribe_ok(moq, moq_stream, buffer, blen, + request_id, track_alias, parameters, track_properties); + imquic_connection_send_on_stream(conn, + moq_stream ? moq_stream->stream_id : moq->control_stream_id, buffer, sb_len, FALSE); /* Done */ imquic_refcount_decrease(&moq->ref); @@ -7273,7 +6230,7 @@ int imquic_moq_accept_subscribe(imquic_connection *conn, uint64_t request_id, ui } int imquic_moq_reject_subscribe(imquic_connection *conn, uint64_t request_id, - imquic_moq_request_error_code error_code, const char *reason, uint64_t track_alias, uint64_t retry_interval) { + imquic_moq_request_error_code error_code, const char *reason, uint64_t retry_interval) { imquic_mutex_lock(&moq_mutex); imquic_moq_context *moq = g_hash_table_lookup(moq_sessions, conn); if(moq == NULL || (reason && strlen(reason) > 1024)) { @@ -7284,23 +6241,41 @@ int imquic_moq_reject_subscribe(imquic_connection *conn, uint64_t request_id, } imquic_refcount_increase(&moq->ref); imquic_mutex_unlock(&moq_mutex); - /* TODO Check if we were subscribed */ + /* Starting from v17, requests go on a dedicated bidirectional + * STREAM, and the same applies to the REQUEST_ERROR responses */ + imquic_moq_stream *moq_stream = NULL; + if(moq->version >= IMQUIC_MOQ_VERSION_17) { + imquic_mutex_lock(&moq->mutex); + moq_stream = g_hash_table_lookup(moq->streams_by_reqid, &request_id); + if(moq_stream == NULL || moq_stream->request_type != IMQUIC_MOQ_SUBSCRIBE || moq_stream->request_sender || + moq_stream->request_state != IMQUIC_MOQ_REQUEST_STATE_SENT) { + IMQUIC_LOG(IMQUIC_LOG_ERR, "[%s][MoQ] Invalid request/state (%s)\n", + imquic_get_connection_name(conn), moq_stream ? imquic_media_stream_request_state_str(moq_stream->request_state) : "No stream"); + imquic_mutex_unlock(&moq->mutex); + imquic_refcount_decrease(&moq->ref); + return -1; + } + moq_stream->request_state = IMQUIC_MOQ_REQUEST_STATE_ERROR; + imquic_mutex_unlock(&moq->mutex); + } uint8_t buffer[200]; size_t blen = sizeof(buffer); - size_t sb_len = 0; - if(moq->version <= IMQUIC_MOQ_VERSION_14) { - sb_len = imquic_moq_add_subscribe_error(moq, buffer, blen, request_id, error_code, reason, track_alias); - } else { - sb_len = imquic_moq_add_request_error(moq, NULL, buffer, blen, request_id, error_code, reason, retry_interval); + size_t sb_len = imquic_moq_add_request_error(moq, moq_stream, buffer, blen, request_id, error_code, reason, retry_interval); + imquic_connection_send_on_stream(conn, + moq_stream ? moq_stream->stream_id : moq->control_stream_id, + buffer, sb_len, moq_stream ? TRUE : FALSE); + if(moq_stream != NULL) { + imquic_mutex_lock(&moq->mutex); + g_hash_table_remove(moq->streams_by_reqid, &moq_stream->request_id); + g_hash_table_remove(moq->streams, &moq_stream->stream_id); + imquic_mutex_unlock(&moq->mutex); } - imquic_connection_send_on_stream(conn, moq->control_stream_id, - buffer, sb_len, FALSE); /* Done */ imquic_refcount_decrease(&moq->ref); return 0; } -int imquic_moq_update_request(imquic_connection *conn, uint64_t request_id, uint64_t sub_request_id, imquic_moq_request_parameters *parameters) { +int imquic_moq_update_request(imquic_connection *conn, uint64_t request_id, uint64_t sub_request_id, uint64_t required_id_delta, imquic_moq_request_parameters *parameters) { imquic_mutex_lock(&moq_mutex); imquic_moq_context *moq = g_hash_table_lookup(moq_sessions, conn); if(moq == NULL) { @@ -7309,27 +6284,6 @@ int imquic_moq_update_request(imquic_connection *conn, uint64_t request_id, uint imquic_mutex_unlock(&moq_mutex); return -1; } - if(moq->version <= IMQUIC_MOQ_VERSION_14 && parameters == NULL) { - IMQUIC_LOG(IMQUIC_LOG_ERR, "[%s][MoQ] Invalid arguments (missing mandatory parameters)\n", - imquic_get_connection_name(conn)); - imquic_mutex_unlock(&moq_mutex); - return -1; - } - if(moq->version <= IMQUIC_MOQ_VERSION_14 && !parameters->forward_set) { - /* Force some defaults */ - parameters->forward_set = TRUE; - parameters->forward = TRUE; - } - if(moq->version <= IMQUIC_MOQ_VERSION_14 && !parameters->subscriber_priority_set) { - /* Force some defaults */ - parameters->subscriber_priority_set = TRUE; - parameters->subscriber_priority = 128; - } - if(moq->version <= IMQUIC_MOQ_VERSION_14 && !parameters->subscription_filter_set) { - /* Force some defaults */ - parameters->subscription_filter_set = TRUE; - parameters->subscription_filter.type = IMQUIC_MOQ_FILTER_LARGEST_OBJECT; - } imquic_refcount_increase(&moq->ref); imquic_mutex_unlock(&moq_mutex); if(parameters && parameters->subscription_filter_set && parameters->subscription_filter.type == IMQUIC_MOQ_FILTER_ABSOLUTE_RANGE && @@ -7341,19 +6295,35 @@ int imquic_moq_update_request(imquic_connection *conn, uint64_t request_id, uint imquic_refcount_decrease(&moq->ref); return -1; } - /* TODO Check if we were subscribed */ - if(moq->version >= IMQUIC_MOQ_VERSION_15) { - /* Map this request ID to this message type, so that we can trigger - * the right application callbac if/when we get a response later on */ + /* Map this request ID to this message type, so that we can trigger + * the right application callbac if/when we get a response later on */ + imquic_mutex_lock(&moq->mutex); + g_hash_table_insert(moq->requests, imquic_dup_uint64(request_id), GUINT_TO_POINTER(IMQUIC_MOQ_REQUEST_UPDATE)); + imquic_mutex_unlock(&moq->mutex); + /* Starting from v17, requests go on a dedicated bidirectional + * STREAM, and the same applies to the REQUEST_UPDATE responses */ + imquic_moq_stream *moq_stream = NULL; + if(moq->version >= IMQUIC_MOQ_VERSION_17) { imquic_mutex_lock(&moq->mutex); - g_hash_table_insert(moq->requests, imquic_dup_uint64(request_id), GUINT_TO_POINTER(IMQUIC_MOQ_REQUEST_UPDATE)); + moq_stream = g_hash_table_lookup(moq->streams_by_reqid, &sub_request_id); + if(moq_stream == NULL || moq_stream->request_type == 0 || !moq_stream->request_sender || + moq_stream->request_state != IMQUIC_MOQ_REQUEST_STATE_OK) { + IMQUIC_LOG(IMQUIC_LOG_ERR, "[%s][MoQ] Invalid request/state (%s)\n", + imquic_get_connection_name(conn), moq_stream ? imquic_media_stream_request_state_str(moq_stream->request_state) : "No stream"); + imquic_mutex_unlock(&moq->mutex); + imquic_refcount_decrease(&moq->ref); + return -1; + } + moq_stream->update_request_id = request_id; + moq_stream->request_state = IMQUIC_MOQ_REQUEST_STATE_UPDATE_SENT; imquic_mutex_unlock(&moq->mutex); } uint8_t buffer[200]; size_t blen = sizeof(buffer); - size_t su_len = imquic_moq_add_request_update(moq, buffer, blen, - request_id, sub_request_id, parameters); - imquic_connection_send_on_stream(conn, moq->control_stream_id, + size_t su_len = imquic_moq_add_request_update(moq, moq_stream, buffer, blen, + request_id, sub_request_id, required_id_delta, parameters); + imquic_connection_send_on_stream(conn, + moq_stream ? moq_stream->stream_id : moq->control_stream_id, buffer, su_len, FALSE); /* Done */ imquic_refcount_decrease(&moq->ref); @@ -7369,21 +6339,42 @@ int imquic_moq_accept_request_update(imquic_connection *conn, uint64_t request_i imquic_mutex_unlock(&moq_mutex); return -1; } - if(moq->version < IMQUIC_MOQ_VERSION_15) { - IMQUIC_LOG(IMQUIC_LOG_WARN, "[%s][MoQ] Can't send %s acknowledgements on a connection using %s\n", - imquic_get_connection_name(moq->conn), imquic_moq_message_type_str(IMQUIC_MOQ_REQUEST_UPDATE, moq->version), - imquic_moq_version_str(moq->version)); - imquic_mutex_unlock(&moq_mutex); - return -1; - } imquic_refcount_increase(&moq->ref); imquic_mutex_unlock(&moq_mutex); - /* TODO Check if the request ID exists */ - /* TODO Check if we were subscribed */ + /* Check which request this update refers to */ + imquic_mutex_lock(&moq->mutex); + uint64_t *rid = g_hash_table_lookup(moq->update_requests, &request_id); + if(rid == NULL) { + imquic_mutex_unlock(&moq->mutex); + IMQUIC_LOG(IMQUIC_LOG_ERR, "[%s][MoQ] Invalid arguments\n", + imquic_get_connection_name(conn)); + return -1; + } + uint64_t sub_request_id = *rid; + g_hash_table_remove(moq->update_requests, &request_id); + imquic_mutex_unlock(&moq->mutex); + /* Starting from v17, requests go on a dedicated bidirectional + * STREAM, and the same applies to the REQUEST_OK responses */ + imquic_moq_stream *moq_stream = NULL; + if(moq->version >= IMQUIC_MOQ_VERSION_17) { + imquic_mutex_lock(&moq->mutex); + moq_stream = g_hash_table_lookup(moq->streams_by_reqid, &sub_request_id); + if(moq_stream == NULL || moq_stream->request_type == 0 || moq_stream->request_sender || + moq_stream->request_state != IMQUIC_MOQ_REQUEST_STATE_UPDATE_SENT) { + IMQUIC_LOG(IMQUIC_LOG_ERR, "[%s][MoQ] Invalid request/state (%s)\n", + imquic_get_connection_name(conn), moq_stream ? imquic_media_stream_request_state_str(moq_stream->request_state) : "No stream"); + imquic_mutex_unlock(&moq->mutex); + imquic_refcount_decrease(&moq->ref); + return -1; + } + moq_stream->request_state = IMQUIC_MOQ_REQUEST_STATE_OK; + imquic_mutex_unlock(&moq->mutex); + } uint8_t buffer[200]; size_t blen = sizeof(buffer); - size_t sb_len = imquic_moq_add_request_ok(moq, NULL, buffer, blen, request_id, parameters); - imquic_connection_send_on_stream(conn, moq->control_stream_id, + size_t sb_len = imquic_moq_add_request_ok(moq, moq_stream, buffer, blen, request_id, parameters); + imquic_connection_send_on_stream(conn, + moq_stream ? moq_stream->stream_id : moq->control_stream_id, buffer, sb_len, FALSE); /* Done */ imquic_refcount_decrease(&moq->ref); @@ -7400,20 +6391,43 @@ int imquic_moq_reject_request_update(imquic_connection *conn, uint64_t request_i imquic_mutex_unlock(&moq_mutex); return -1; } - if(moq->version < IMQUIC_MOQ_VERSION_15) { - IMQUIC_LOG(IMQUIC_LOG_WARN, "[%s][MoQ] Can't send %s errors on a connection using %s\n", - imquic_get_connection_name(moq->conn), imquic_moq_message_type_str(IMQUIC_MOQ_REQUEST_UPDATE, moq->version), - imquic_moq_version_str(moq->version)); - imquic_mutex_unlock(&moq_mutex); - return -1; - } imquic_refcount_increase(&moq->ref); imquic_mutex_unlock(&moq_mutex); - /* TODO Check if we were subscribed */ + /* Check which request this update refers to */ + imquic_mutex_lock(&moq->mutex); + uint64_t *rid = g_hash_table_lookup(moq->update_requests, &request_id); + if(rid == NULL) { + imquic_mutex_unlock(&moq->mutex); + IMQUIC_LOG(IMQUIC_LOG_ERR, "[%s][MoQ] Invalid arguments\n", + imquic_get_connection_name(conn)); + return -1; + } + uint64_t sub_request_id = *rid; + g_hash_table_remove(moq->update_requests, &request_id); + imquic_mutex_unlock(&moq->mutex); + /* Starting from v17, requests go on a dedicated bidirectional + * STREAM, and the same applies to the REQUEST_ERROR responses */ + imquic_moq_stream *moq_stream = NULL; + if(moq->version >= IMQUIC_MOQ_VERSION_17) { + imquic_mutex_lock(&moq->mutex); + moq_stream = g_hash_table_lookup(moq->streams_by_reqid, &sub_request_id); + if(moq_stream == NULL || moq_stream->request_type == 0 || moq_stream->request_sender || + moq_stream->request_state != IMQUIC_MOQ_REQUEST_STATE_UPDATE_SENT) { + IMQUIC_LOG(IMQUIC_LOG_ERR, "[%s][MoQ] Invalid request/state (%s)\n", + imquic_get_connection_name(conn), moq_stream ? imquic_media_stream_request_state_str(moq_stream->request_state) : "No stream"); + imquic_mutex_unlock(&moq->mutex); + imquic_refcount_decrease(&moq->ref); + return -1; + } + /* FIXME We mark the state as OK, as this is an update */ + moq_stream->request_state = IMQUIC_MOQ_REQUEST_STATE_OK; + imquic_mutex_unlock(&moq->mutex); + } uint8_t buffer[200]; size_t blen = sizeof(buffer); - size_t sb_len = imquic_moq_add_request_error(moq, NULL, buffer, blen, request_id, error_code, reason, retry_interval); - imquic_connection_send_on_stream(conn, moq->control_stream_id, + size_t sb_len = imquic_moq_add_request_error(moq, moq_stream, buffer, blen, request_id, error_code, reason, retry_interval); + imquic_connection_send_on_stream(conn, + moq_stream ? moq_stream->stream_id : moq->control_stream_id, buffer, sb_len, FALSE); /* Done */ imquic_refcount_decrease(&moq->ref); @@ -7431,7 +6445,29 @@ int imquic_moq_unsubscribe(imquic_connection *conn, uint64_t request_id) { } imquic_refcount_increase(&moq->ref); imquic_mutex_unlock(&moq_mutex); - /* TODO Check if we were subscribed */ + /* Check if we have a STREAM to close */ + if(moq->version >= IMQUIC_MOQ_VERSION_17) { + /* On newer versions of MoQ, requests uses a dedicated + * bidirectional STREAM, so we simply close the STREAM */ + imquic_mutex_lock(&moq->mutex); + imquic_moq_stream *moq_stream = g_hash_table_lookup(moq->streams_by_reqid, &request_id); + if(moq_stream == NULL || moq_stream->request_type != IMQUIC_MOQ_SUBSCRIBE || !moq_stream->request_sender) { + IMQUIC_LOG(IMQUIC_LOG_ERR, "[%s][MoQ] Invalid request/state (%s)\n", + imquic_get_connection_name(conn), moq_stream ? imquic_media_stream_request_state_str(moq_stream->request_state) : "No stream"); + imquic_mutex_unlock(&moq->mutex); + imquic_refcount_decrease(&moq->ref); + return -1; + } + /* Send a STOP_SENDING */ + uint64_t stream_id = moq_stream->stream_id; + g_hash_table_remove(moq->streams_by_reqid, &moq_stream->request_id); + g_hash_table_remove(moq->streams, &moq_stream->stream_id); + imquic_mutex_unlock(&moq->mutex); + imquic_connection_stop_sending_stream(moq->conn, stream_id, IMQUIC_MOQ_RESET_CANCELLED); + imquic_refcount_decrease(&moq->ref); + return 0; + } + /* If we're here,we're sending the legacy UNSUBSCRIBE */ uint8_t buffer[200]; size_t blen = sizeof(buffer); size_t sb_len = imquic_moq_add_unsubscribe(moq, buffer, blen, request_id); @@ -7465,23 +6501,45 @@ int imquic_moq_publish_done(imquic_connection *conn, uint64_t request_id, imquic } uint64_t streams_count = moq_sub->streams_count; imquic_mutex_unlock(&moq->mutex); + /* Starting from v17, requests go on a dedicated bidirectional + * STREAM, and the same applies to the PUBLISH_DONE responses */ + imquic_moq_stream *moq_stream = NULL; + if(moq->version >= IMQUIC_MOQ_VERSION_17) { + imquic_mutex_lock(&moq->mutex); + moq_stream = g_hash_table_lookup(moq->streams_by_reqid, &request_id); + if(moq_stream == NULL || moq_stream->request_type != IMQUIC_MOQ_PUBLISH || !moq_stream->request_sender || + (moq_stream->request_state != IMQUIC_MOQ_REQUEST_STATE_OK && moq_stream->request_state != IMQUIC_MOQ_REQUEST_STATE_UPDATE_SENT)) { + IMQUIC_LOG(IMQUIC_LOG_ERR, "[%s][MoQ] Invalid request/state (%s)\n", + imquic_get_connection_name(conn), moq_stream ? imquic_media_stream_request_state_str(moq_stream->request_state) : "No stream"); + imquic_mutex_unlock(&moq->mutex); + imquic_refcount_decrease(&moq->ref); + return -1; + } + imquic_mutex_unlock(&moq->mutex); + } uint8_t buffer[200]; size_t blen = sizeof(buffer); - /* TODO Compute streams count */ - size_t sd_len = imquic_moq_add_publish_done(moq, buffer, blen, + size_t sd_len = imquic_moq_add_publish_done(moq, moq_stream, buffer, blen, request_id, status_code, streams_count, reason); - imquic_connection_send_on_stream(conn, moq->control_stream_id, - buffer, sd_len, FALSE); + imquic_connection_send_on_stream(conn, + moq_stream ? moq_stream->stream_id : moq->control_stream_id, + buffer, sd_len, moq_stream ? TRUE : FALSE); + if(moq_stream != NULL) { + imquic_mutex_lock(&moq->mutex); + g_hash_table_remove(moq->streams_by_reqid, &moq_stream->request_id); + g_hash_table_remove(moq->streams, &moq_stream->stream_id); + imquic_mutex_unlock(&moq->mutex); + } /* Done */ imquic_refcount_decrease(&moq->ref); return 0; } -int imquic_moq_subscribe_namespace(imquic_connection *conn, uint64_t request_id, +int imquic_moq_subscribe_namespace(imquic_connection *conn, uint64_t request_id, uint64_t required_id_delta, imquic_moq_namespace *tns, imquic_moq_subscribe_namespace_options subscribe_options, imquic_moq_request_parameters *parameters) { imquic_mutex_lock(&moq_mutex); imquic_moq_context *moq = g_hash_table_lookup(moq_sessions, conn); - if(moq == NULL || tns == NULL || tns->buffer == 0 || tns->length == 0) { + if(moq == NULL) { IMQUIC_LOG(IMQUIC_LOG_ERR, "[%s][MoQ] Invalid arguments\n", imquic_get_connection_name(conn)); imquic_mutex_unlock(&moq_mutex); @@ -7508,47 +6566,37 @@ int imquic_moq_subscribe_namespace(imquic_connection *conn, uint64_t request_id, moq->next_request_id = request_id + IMQUIC_MOQ_REQUEST_ID_INCREMENT; imquic_refcount_increase(&moq->ref); imquic_mutex_unlock(&moq_mutex); - /* Send the request */ - if(moq->version >= IMQUIC_MOQ_VERSION_15) { - /* Map this request ID to this message type, so that we can trigger - * the right application callback if/when we get a response later on */ - imquic_mutex_lock(&moq->mutex); - g_hash_table_insert(moq->requests, imquic_dup_uint64(request_id), GUINT_TO_POINTER(IMQUIC_MOQ_SUBSCRIBE_NAMESPACE)); - imquic_mutex_unlock(&moq->mutex); - } - imquic_moq_stream *moq_stream = NULL; - if(moq->version >= IMQUIC_MOQ_VERSION_16) { - /* Starting from v16, SUBSCRIBE_NAMESPACE goes on a dedicated bidirectional STREAM */ - moq_stream = g_malloc0(sizeof(imquic_moq_stream)); - imquic_connection_new_stream_id(moq->conn, TRUE, &moq_stream->stream_id); - moq_stream->subscribe_namespace = TRUE; - imquic_mutex_lock(&moq->mutex); - g_hash_table_insert(moq->streams, imquic_dup_uint64(moq_stream->stream_id), moq_stream); - imquic_mutex_unlock(&moq->mutex); - } + /* Map this request ID to this message type, so that we can trigger + * the right application callback if/when we get a response later on */ + imquic_mutex_lock(&moq->mutex); + g_hash_table_insert(moq->requests, imquic_dup_uint64(request_id), GUINT_TO_POINTER(IMQUIC_MOQ_SUBSCRIBE_NAMESPACE)); + imquic_mutex_unlock(&moq->mutex); + /* Starting from v16, SUBSCRIBE_NAMESPACE goes on a dedicated bidirectional STREAM */ + imquic_moq_stream *moq_stream = imquic_moq_stream_create(); + imquic_connection_new_stream_id(moq->conn, TRUE, &moq_stream->stream_id); + moq_stream->request_type = IMQUIC_MOQ_SUBSCRIBE_NAMESPACE; + moq_stream->request_id = request_id; + moq_stream->request_sender = TRUE; + imquic_mutex_lock(&moq->mutex); + g_hash_table_insert(moq->streams, imquic_dup_uint64(moq_stream->stream_id), moq_stream); + imquic_mutex_unlock(&moq->mutex); uint8_t buffer[200]; size_t blen = sizeof(buffer); size_t sb_len = 0; - sb_len = imquic_moq_add_subscribe_namespace(moq, moq_stream, buffer, blen, request_id, tns, subscribe_options, parameters); - if(moq->version >= IMQUIC_MOQ_VERSION_16) { - /* Track the request, and map it to the dedicated bidirectional STREAM */ - g_atomic_int_set(&moq_stream->subscribe_namespace_state, 1); - moq_stream->request_id = request_id; - moq_stream->namespace_prefix = moq_stream->last_tuple = imquic_moq_namespace_duplicate(tns); - while(moq_stream->last_tuple->next != NULL) - moq_stream->last_tuple = moq_stream->last_tuple->next; - moq_stream->namespace_prefix_size = tns_num; - imquic_mutex_lock(&moq->mutex); - g_hash_table_insert(moq->tns_subscriptions_by_id, imquic_dup_uint64(request_id), moq_stream); - imquic_mutex_unlock(&moq->mutex); - /* Send on the dedicated bidirectional STREAM */ - imquic_connection_send_on_stream(conn, moq_stream->stream_id, - buffer, sb_len, FALSE); - } else { - /* Older MoQ version, use the control stream */ - imquic_connection_send_on_stream(conn, moq->control_stream_id, - buffer, sb_len, FALSE); - } + sb_len = imquic_moq_add_subscribe_namespace(moq, moq_stream, buffer, blen, request_id, required_id_delta, tns, subscribe_options, parameters); + /* Track the request, and map it to the dedicated bidirectional STREAM */ + moq_stream->request_state = IMQUIC_MOQ_REQUEST_STATE_SENT; + moq_stream->request_id = request_id; + moq_stream->namespace_prefix = moq_stream->last_tuple = tns ? imquic_moq_namespace_duplicate(tns) : g_malloc0(sizeof(imquic_moq_namespace)); + while(moq_stream->last_tuple != NULL && moq_stream->last_tuple->next != NULL) + moq_stream->last_tuple = moq_stream->last_tuple->next; + moq_stream->namespace_prefix_size = tns_num; + imquic_mutex_lock(&moq->mutex); + g_hash_table_insert(moq->streams_by_reqid, imquic_dup_uint64(request_id), moq_stream); + imquic_mutex_unlock(&moq->mutex); + /* Send on the dedicated bidirectional STREAM */ + imquic_connection_send_on_stream(conn, moq_stream->stream_id, + buffer, sb_len, FALSE); /* Done */ imquic_refcount_decrease(&moq->ref); return 0; @@ -7563,42 +6611,28 @@ int imquic_moq_accept_subscribe_namespace(imquic_connection *conn, uint64_t requ imquic_mutex_unlock(&moq_mutex); return -1; } - imquic_refcount_increase(&moq->ref); - imquic_mutex_unlock(&moq_mutex); - /* Check if the request ID exists */ - imquic_moq_stream *moq_stream = NULL; - if(moq->version >= IMQUIC_MOQ_VERSION_16) { - /* Starting from v16, SUBSCRIBE_NAMESPACE goes on a dedicated - * bidirectional STREAM, and the same applies to REQUEST_OK/ERROR */ - imquic_mutex_lock(&moq->mutex); - moq_stream = g_hash_table_lookup(moq->tns_subscriptions_by_id, &request_id); - if(moq_stream == NULL || !moq_stream->subscribe_namespace || !moq_stream->namespace_publisher || - !g_atomic_int_compare_and_exchange(&moq_stream->subscribe_namespace_state, 1, 2)) { - imquic_mutex_unlock(&moq->mutex); - IMQUIC_LOG(IMQUIC_LOG_ERR, "[%s][MoQ] Invalid request/state\n", - imquic_get_connection_name(conn)); - imquic_mutex_unlock(&moq_mutex); - return -1; - } + imquic_refcount_increase(&moq->ref); + imquic_mutex_unlock(&moq_mutex); + /* Starting from v16, SUBSCRIBE_NAMESPACE goes on a dedicated + * bidirectional STREAM, and the same applies to REQUEST_OK/ERROR */ + imquic_mutex_lock(&moq->mutex); + imquic_moq_stream *moq_stream = g_hash_table_lookup(moq->streams_by_reqid, &request_id); + if(moq_stream == NULL || moq_stream->request_type != IMQUIC_MOQ_SUBSCRIBE_NAMESPACE || moq_stream->request_sender || + moq_stream->request_state != IMQUIC_MOQ_REQUEST_STATE_SENT) { + IMQUIC_LOG(IMQUIC_LOG_ERR, "[%s][MoQ] Invalid request/state (%s)\n", + imquic_get_connection_name(conn), moq_stream ? imquic_media_stream_request_state_str(moq_stream->request_state) : "No stream"); imquic_mutex_unlock(&moq->mutex); + imquic_refcount_decrease(&moq->ref); + return -1; } + moq_stream->request_state = IMQUIC_MOQ_REQUEST_STATE_OK; + imquic_mutex_unlock(&moq->mutex); uint8_t buffer[200]; size_t blen = sizeof(buffer); - size_t sb_len = 0; - if(moq->version <= IMQUIC_MOQ_VERSION_14) { - sb_len = imquic_moq_add_subscribe_namespace_ok(moq, buffer, blen, request_id); - } else { - sb_len = imquic_moq_add_request_ok(moq, moq_stream, buffer, blen, request_id, parameters); - } - if(moq_stream != NULL) { - /* Send on the dedicated bidirectional STREAM */ - imquic_connection_send_on_stream(conn, moq_stream->stream_id, - buffer, sb_len, FALSE); - } else { - /* Older MoQ version, use the control stream */ - imquic_connection_send_on_stream(conn, moq->control_stream_id, - buffer, sb_len, FALSE); - } + size_t sb_len = imquic_moq_add_request_ok(moq, moq_stream, buffer, blen, request_id, parameters); + /* Send on the dedicated bidirectional STREAM */ + imquic_connection_send_on_stream(conn, moq_stream->stream_id, + buffer, sb_len, FALSE); /* Done */ imquic_refcount_decrease(&moq->ref); return 0; @@ -7616,49 +6650,41 @@ int imquic_moq_reject_subscribe_namespace(imquic_connection *conn, uint64_t requ } imquic_refcount_increase(&moq->ref); imquic_mutex_unlock(&moq_mutex); - /* Check if the request ID exists */ - imquic_moq_stream *moq_stream = NULL; - if(moq->version >= IMQUIC_MOQ_VERSION_16) { - /* Starting from v16, SUBSCRIBE_NAMESPACE goes on a dedicated - * bidirectional STREAM, and the same applies to REQUEST_OK/ERROR */ - imquic_mutex_lock(&moq->mutex); - moq_stream = g_hash_table_lookup(moq->tns_subscriptions_by_id, &request_id); - if(moq_stream == NULL || !moq_stream->subscribe_namespace || !moq_stream->namespace_publisher || - !g_atomic_int_compare_and_exchange(&moq_stream->subscribe_namespace_state, 1, 2)) { - imquic_mutex_unlock(&moq->mutex); - IMQUIC_LOG(IMQUIC_LOG_ERR, "[%s][MoQ] Invalid request/state\n", - imquic_get_connection_name(conn)); - imquic_mutex_unlock(&moq_mutex); - return -1; - } + /* Starting from v16, SUBSCRIBE_NAMESPACE goes on a dedicated + * bidirectional STREAM, and the same applies to REQUEST_OK/ERROR */ + imquic_mutex_lock(&moq->mutex); + imquic_moq_stream *moq_stream = g_hash_table_lookup(moq->streams_by_reqid, &request_id); + if(moq_stream == NULL || moq_stream->request_type != IMQUIC_MOQ_SUBSCRIBE_NAMESPACE || moq_stream->request_sender || + moq_stream->request_state != IMQUIC_MOQ_REQUEST_STATE_SENT) { + IMQUIC_LOG(IMQUIC_LOG_ERR, "[%s][MoQ] Invalid request/state (%s)\n", + imquic_get_connection_name(conn), moq_stream ? imquic_media_stream_request_state_str(moq_stream->request_state) : "No stream"); imquic_mutex_unlock(&moq->mutex); + imquic_refcount_decrease(&moq->ref); + return -1; } + moq_stream->request_state = IMQUIC_MOQ_REQUEST_STATE_ERROR; + imquic_mutex_unlock(&moq->mutex); uint8_t buffer[200]; size_t blen = sizeof(buffer); - size_t sb_len = 0; - if(moq->version <= IMQUIC_MOQ_VERSION_14) { - sb_len = imquic_moq_add_subscribe_namespace_error(moq, buffer, blen, request_id, error_code, reason); - } else { - sb_len = imquic_moq_add_request_error(moq, moq_stream, buffer, blen, request_id, error_code, reason, retry_interval); - } + size_t sb_len = imquic_moq_add_request_error(moq, moq_stream, buffer, blen, request_id, error_code, reason, retry_interval); + /* Send on the dedicated bidirectional STREAM */ + imquic_connection_send_on_stream(conn, moq_stream->stream_id, + buffer, sb_len, moq_stream ? TRUE : FALSE); if(moq_stream != NULL) { - /* Send on the dedicated bidirectional STREAM */ - imquic_connection_send_on_stream(conn, moq_stream->stream_id, - buffer, sb_len, FALSE); - } else { - /* Older MoQ version, use the control stream */ - imquic_connection_send_on_stream(conn, moq->control_stream_id, - buffer, sb_len, FALSE); + imquic_mutex_lock(&moq->mutex); + g_hash_table_remove(moq->streams_by_reqid, &moq_stream->request_id); + g_hash_table_remove(moq->streams, &moq_stream->stream_id); + imquic_mutex_unlock(&moq->mutex); } /* Done */ imquic_refcount_decrease(&moq->ref); return 0; } -int imquic_moq_unsubscribe_namespace(imquic_connection *conn, uint64_t request_id, imquic_moq_namespace *tns) { +int imquic_moq_unsubscribe_namespace(imquic_connection *conn, uint64_t request_id) { imquic_mutex_lock(&moq_mutex); imquic_moq_context *moq = g_hash_table_lookup(moq_sessions, conn); - if(moq == NULL || (moq->version <= IMQUIC_MOQ_VERSION_14 && (tns == NULL || tns->buffer == 0 || tns->length == 0))) { + if(moq == NULL) { IMQUIC_LOG(IMQUIC_LOG_ERR, "[%s][MoQ] Invalid arguments\n", imquic_get_connection_name(conn)); imquic_mutex_unlock(&moq_mutex); @@ -7666,35 +6692,24 @@ int imquic_moq_unsubscribe_namespace(imquic_connection *conn, uint64_t request_i } imquic_refcount_increase(&moq->ref); imquic_mutex_unlock(&moq_mutex); - /* Check if we were subscribed */ - if(moq->version >= IMQUIC_MOQ_VERSION_16) { - /* On newer versions of MoQ, SUBSCRIBE_NAMESPACE uses a dedicated - * bidirectional STREAM, so unsubscribing is done without sending - * any actual message: we simply close the STREAM */ - imquic_mutex_lock(&moq->mutex); - imquic_moq_stream *moq_stream = g_hash_table_lookup(moq->tns_subscriptions_by_id, &request_id); - if(moq_stream == NULL) { - IMQUIC_LOG(IMQUIC_LOG_ERR, "[%s][MoQ] Invalid arguments: no such subscription\n", - imquic_get_connection_name(conn)); - imquic_mutex_unlock(&moq->mutex); - imquic_refcount_decrease(&moq->ref); - return -1; - } - /* Reset the STREAM */ - imquic_connection_reset_stream(moq->conn, moq_stream->stream_id, IMQUIC_MOQ_RESET_CANCELLED); - g_hash_table_remove(moq->tns_subscriptions_by_id, &request_id); - g_hash_table_remove(moq->streams, &moq_stream->stream_id); + /* On newer versions of MoQ, SUBSCRIBE_NAMESPACE uses a dedicated + * bidirectional STREAM, so unsubscribing is done without sending + * any actual message: we simply close the STREAM */ + imquic_mutex_lock(&moq->mutex); + imquic_moq_stream *moq_stream = g_hash_table_lookup(moq->streams_by_reqid, &request_id); + if(moq_stream == NULL || moq_stream->request_type != IMQUIC_MOQ_SUBSCRIBE_NAMESPACE || + !moq_stream->request_sender) { + IMQUIC_LOG(IMQUIC_LOG_ERR, "[%s][MoQ] Invalid arguments: no such subscription\n", + imquic_get_connection_name(conn)); imquic_mutex_unlock(&moq->mutex); imquic_refcount_decrease(&moq->ref); - return 0; + return -1; } - /* If we're here, we're sending the legacy UNSUBSCRIBE_NAMESPACE */ - uint8_t buffer[200]; - size_t blen = sizeof(buffer); - size_t sb_len = imquic_moq_add_unsubscribe_namespace(moq, buffer, blen, request_id, tns); - imquic_connection_send_on_stream(conn, moq->control_stream_id, - buffer, sb_len, FALSE); - /* Done */ + /* Reset the STREAM */ + imquic_connection_reset_stream(moq->conn, moq_stream->stream_id, IMQUIC_MOQ_RESET_CANCELLED); + g_hash_table_remove(moq->streams_by_reqid, &request_id); + g_hash_table_remove(moq->streams, &moq_stream->stream_id); + imquic_mutex_unlock(&moq->mutex); imquic_refcount_decrease(&moq->ref); return 0; } @@ -7702,7 +6717,7 @@ int imquic_moq_unsubscribe_namespace(imquic_connection *conn, uint64_t request_i int imquic_moq_notify_namespace(imquic_connection *conn, uint64_t request_id, imquic_moq_namespace *tns) { imquic_mutex_lock(&moq_mutex); imquic_moq_context *moq = g_hash_table_lookup(moq_sessions, conn); - if(moq == NULL || moq->version < IMQUIC_MOQ_VERSION_16 || tns == NULL || tns->buffer == 0 || tns->length == 0) { + if(moq == NULL || tns == NULL || tns->buffer == 0 || tns->length == 0) { IMQUIC_LOG(IMQUIC_LOG_ERR, "[%s][MoQ] Invalid arguments\n", imquic_get_connection_name(conn)); imquic_mutex_unlock(&moq_mutex); @@ -7712,13 +6727,14 @@ int imquic_moq_notify_namespace(imquic_connection *conn, uint64_t request_id, im imquic_mutex_unlock(&moq_mutex); /* Check if the request ID exists */ imquic_mutex_lock(&moq->mutex); - imquic_moq_stream *moq_stream = g_hash_table_lookup(moq->tns_subscriptions_by_id, &request_id); - if(moq_stream == NULL || !moq_stream->subscribe_namespace || !moq_stream->namespace_publisher || - g_atomic_int_get(&moq_stream->subscribe_namespace_state) < 2 || + imquic_moq_stream *moq_stream = g_hash_table_lookup(moq->streams_by_reqid, &request_id); + if(moq_stream == NULL || moq_stream->request_type != IMQUIC_MOQ_SUBSCRIBE_NAMESPACE || moq_stream->request_sender || + (moq_stream->request_state != IMQUIC_MOQ_REQUEST_STATE_OK && moq_stream->request_state != IMQUIC_MOQ_REQUEST_STATE_UPDATE_SENT) || !imquic_moq_namespace_contains(moq_stream->namespace_prefix, tns)) { + IMQUIC_LOG(IMQUIC_LOG_ERR, "[%s][MoQ] Invalid request/state (%s)\n", + imquic_get_connection_name(conn), moq_stream ? imquic_media_stream_request_state_str(moq_stream->request_state) : "No stream"); imquic_mutex_unlock(&moq->mutex); - IMQUIC_LOG(IMQUIC_LOG_ERR, "[%s][MoQ] Invalid request/state\n", - imquic_get_connection_name(conn)); + imquic_refcount_decrease(&moq->ref); return -1; } imquic_mutex_unlock(&moq->mutex); @@ -7741,7 +6757,7 @@ int imquic_moq_notify_namespace(imquic_connection *conn, uint64_t request_id, im int imquic_moq_notify_namespace_done(imquic_connection *conn, uint64_t request_id, imquic_moq_namespace *tns) { imquic_mutex_lock(&moq_mutex); imquic_moq_context *moq = g_hash_table_lookup(moq_sessions, conn); - if(moq == NULL || moq->version < IMQUIC_MOQ_VERSION_16 || tns == NULL || tns->buffer == 0 || tns->length == 0) { + if(moq == NULL || tns == NULL || tns->buffer == 0 || tns->length == 0) { IMQUIC_LOG(IMQUIC_LOG_ERR, "[%s][MoQ] Invalid arguments\n", imquic_get_connection_name(conn)); imquic_mutex_unlock(&moq_mutex); @@ -7751,14 +6767,14 @@ int imquic_moq_notify_namespace_done(imquic_connection *conn, uint64_t request_i imquic_mutex_unlock(&moq_mutex); /* Check if the request ID exists */ imquic_mutex_lock(&moq->mutex); - imquic_moq_stream *moq_stream = g_hash_table_lookup(moq->tns_subscriptions_by_id, &request_id); - if(moq_stream == NULL || !moq_stream->subscribe_namespace || !moq_stream->namespace_publisher || - g_atomic_int_get(&moq_stream->subscribe_namespace_state) < 2 || + imquic_moq_stream *moq_stream = g_hash_table_lookup(moq->streams_by_reqid, &request_id); + if(moq_stream == NULL || moq_stream->request_type != IMQUIC_MOQ_SUBSCRIBE_NAMESPACE || moq_stream->request_sender || + (moq_stream->request_state != IMQUIC_MOQ_REQUEST_STATE_OK && moq_stream->request_state != IMQUIC_MOQ_REQUEST_STATE_UPDATE_SENT) || !imquic_moq_namespace_contains(moq_stream->namespace_prefix, tns)) { + IMQUIC_LOG(IMQUIC_LOG_ERR, "[%s][MoQ] Invalid request/state (%s)\n", + imquic_get_connection_name(conn), moq_stream ? imquic_media_stream_request_state_str(moq_stream->request_state) : "No stream"); imquic_mutex_unlock(&moq->mutex); - IMQUIC_LOG(IMQUIC_LOG_ERR, "[%s][MoQ] Invalid request/state\n", - imquic_get_connection_name(conn)); - imquic_mutex_unlock(&moq_mutex); + imquic_refcount_decrease(&moq->ref); return -1; } imquic_mutex_unlock(&moq->mutex); @@ -7778,32 +6794,57 @@ int imquic_moq_notify_namespace_done(imquic_connection *conn, uint64_t request_i return 0; } -int imquic_moq_standalone_fetch(imquic_connection *conn, uint64_t request_id, - imquic_moq_namespace *tns, imquic_moq_name *tn, imquic_moq_location_range *range, imquic_moq_request_parameters *parameters) { +int imquic_moq_notify_publish_blocked(imquic_connection *conn, uint64_t request_id, imquic_moq_namespace *tns, imquic_moq_name *tn) { imquic_mutex_lock(&moq_mutex); imquic_moq_context *moq = g_hash_table_lookup(moq_sessions, conn); - if(moq == NULL || tns == NULL || tn == NULL || range == NULL) { + if(moq == NULL || tns == NULL || tns->buffer == 0 || tns->length == 0 || + tn == NULL || tn->buffer == 0 || tn->length == 0) { IMQUIC_LOG(IMQUIC_LOG_ERR, "[%s][MoQ] Invalid arguments\n", imquic_get_connection_name(conn)); imquic_mutex_unlock(&moq_mutex); return -1; } - if(moq->version <= IMQUIC_MOQ_VERSION_14 && parameters == NULL) { - IMQUIC_LOG(IMQUIC_LOG_ERR, "[%s][MoQ] Invalid arguments (missing mandatory parameters)\n", + imquic_refcount_increase(&moq->ref); + imquic_mutex_unlock(&moq_mutex); + /* Check if the request ID exists */ + imquic_mutex_lock(&moq->mutex); + imquic_moq_stream *moq_stream = g_hash_table_lookup(moq->streams_by_reqid, &request_id); + if(moq_stream == NULL || moq_stream->request_type != IMQUIC_MOQ_SUBSCRIBE_NAMESPACE || moq_stream->request_sender || + (moq_stream->request_state != IMQUIC_MOQ_REQUEST_STATE_OK && moq_stream->request_state != IMQUIC_MOQ_REQUEST_STATE_UPDATE_SENT) || + !imquic_moq_namespace_contains(moq_stream->namespace_prefix, tns)) { + IMQUIC_LOG(IMQUIC_LOG_ERR, "[%s][MoQ] Invalid request/state (%s)\n", + imquic_get_connection_name(conn), moq_stream ? imquic_media_stream_request_state_str(moq_stream->request_state) : "No stream"); + imquic_mutex_unlock(&moq->mutex); + imquic_refcount_decrease(&moq->ref); + return -1; + } + imquic_mutex_unlock(&moq->mutex); + /* We need the track namespace suffix */ + imquic_moq_namespace *tns_suffix = tns; + for(uint8_t i=0; inamespace_prefix_size; i++) + tns_suffix = tns_suffix->next; + /* Prepare the message */ + uint8_t buffer[200]; + size_t blen = sizeof(buffer); + size_t nn_len = imquic_moq_add_publish_blocked(moq, moq_stream, buffer, blen, tns_suffix, tn); + /* Send on the dedicated bidirectional STREAM */ + imquic_connection_send_on_stream(conn, moq_stream->stream_id, + buffer, nn_len, FALSE); + /* Done */ + imquic_refcount_decrease(&moq->ref); + return 0; +} + +int imquic_moq_standalone_fetch(imquic_connection *conn, uint64_t request_id, uint64_t required_id_delta, + imquic_moq_namespace *tns, imquic_moq_name *tn, imquic_moq_location_range *range, imquic_moq_request_parameters *parameters) { + imquic_mutex_lock(&moq_mutex); + imquic_moq_context *moq = g_hash_table_lookup(moq_sessions, conn); + if(moq == NULL || tns == NULL || tn == NULL || range == NULL) { + IMQUIC_LOG(IMQUIC_LOG_ERR, "[%s][MoQ] Invalid arguments\n", imquic_get_connection_name(conn)); imquic_mutex_unlock(&moq_mutex); return -1; } - if(moq->version <= IMQUIC_MOQ_VERSION_14 && !parameters->subscriber_priority_set) { - /* Force some defaults */ - parameters->subscriber_priority_set = TRUE; - parameters->subscriber_priority = 128; - } - if(moq->version <= IMQUIC_MOQ_VERSION_14 && !parameters->group_order_set) { - /* Force some defaults */ - parameters->group_order_set = TRUE; - parameters->group_order = IMQUIC_MOQ_ORDERING_ASCENDING; - } /* Make sure we can send this */ if(!moq_is_request_id_valid(moq, request_id, TRUE)) { IMQUIC_LOG(IMQUIC_LOG_ERR, "[%s][MoQ] Invalid Request ID\n", imquic_get_connection_name(conn)); @@ -7813,32 +6854,43 @@ int imquic_moq_standalone_fetch(imquic_connection *conn, uint64_t request_id, moq->next_request_id = request_id + IMQUIC_MOQ_REQUEST_ID_INCREMENT; imquic_refcount_increase(&moq->ref); imquic_mutex_unlock(&moq_mutex); - /* TODO Check if this namespace exists and was publish_namespaced here */ - /* TODO Track subscription and track alias */ - if(moq->version >= IMQUIC_MOQ_VERSION_15) { - /* Map this request ID to this message type, so that we can trigger - * the right application callbac if/when we get a response later on */ + /* Map this request ID to this message type, so that we can trigger + * the right application callbac if/when we get a response later on */ + imquic_mutex_lock(&moq->mutex); + g_hash_table_insert(moq->requests, imquic_dup_uint64(request_id), GUINT_TO_POINTER(IMQUIC_MOQ_FETCH)); + imquic_mutex_unlock(&moq->mutex); + /* Starting from v17, requests on a dedicated bidirectional STREAM */ + imquic_moq_stream *moq_stream = NULL; + if(moq->version >= IMQUIC_MOQ_VERSION_17) { + moq_stream = imquic_moq_stream_create(); + imquic_connection_new_stream_id(moq->conn, TRUE, &moq_stream->stream_id); + moq_stream->request_type = IMQUIC_MOQ_FETCH; + moq_stream->request_id = request_id; + moq_stream->request_sender = TRUE; + moq_stream->request_state = IMQUIC_MOQ_REQUEST_STATE_SENT; imquic_mutex_lock(&moq->mutex); - g_hash_table_insert(moq->requests, imquic_dup_uint64(request_id), GUINT_TO_POINTER(IMQUIC_MOQ_FETCH)); + g_hash_table_insert(moq->streams, imquic_dup_uint64(moq_stream->stream_id), moq_stream); + g_hash_table_insert(moq->streams_by_reqid, imquic_dup_uint64(request_id), moq_stream); imquic_mutex_unlock(&moq->mutex); } uint8_t buffer[200]; size_t blen = sizeof(buffer); size_t f_len = 0; - f_len = imquic_moq_add_fetch(moq, buffer, blen, + f_len = imquic_moq_add_fetch(moq, moq_stream, buffer, blen, IMQUIC_MOQ_FETCH_STANDALONE, - request_id, + request_id, required_id_delta, 0, 0, /* Ignored, as they're only used for Joining Fetch */ tns, tn, range, parameters); - imquic_connection_send_on_stream(conn, moq->control_stream_id, + imquic_connection_send_on_stream(conn, + moq_stream ? moq_stream->stream_id : moq->control_stream_id, buffer, f_len, FALSE); /* Done */ imquic_refcount_decrease(&moq->ref); return 0; } -int imquic_moq_joining_fetch(imquic_connection *conn, uint64_t request_id, uint64_t joining_request_id, +int imquic_moq_joining_fetch(imquic_connection *conn, uint64_t request_id, uint64_t required_id_delta, uint64_t joining_request_id, gboolean absolute, uint64_t joining_start, imquic_moq_request_parameters *parameters) { imquic_mutex_lock(&moq_mutex); imquic_moq_context *moq = g_hash_table_lookup(moq_sessions, conn); @@ -7848,22 +6900,6 @@ int imquic_moq_joining_fetch(imquic_connection *conn, uint64_t request_id, uint6 imquic_mutex_unlock(&moq_mutex); return -1; } - if(moq->version <= IMQUIC_MOQ_VERSION_14 && parameters == NULL) { - IMQUIC_LOG(IMQUIC_LOG_ERR, "[%s][MoQ] Invalid arguments (missing mandatory parameters)\n", - imquic_get_connection_name(conn)); - imquic_mutex_unlock(&moq_mutex); - return -1; - } - if(moq->version <= IMQUIC_MOQ_VERSION_14 && !parameters->subscriber_priority_set) { - /* Force some defaults */ - parameters->subscriber_priority_set = TRUE; - parameters->subscriber_priority = 128; - } - if(moq->version <= IMQUIC_MOQ_VERSION_14 && !parameters->group_order_set) { - /* Force some defaults */ - parameters->group_order_set = TRUE; - parameters->group_order = IMQUIC_MOQ_ORDERING_ASCENDING; - } /* Make sure we can send this */ if(!moq_is_request_id_valid(moq, request_id, TRUE)) { IMQUIC_LOG(IMQUIC_LOG_ERR, "[%s][MoQ] Invalid Request ID\n", imquic_get_connection_name(conn)); @@ -7873,25 +6909,36 @@ int imquic_moq_joining_fetch(imquic_connection *conn, uint64_t request_id, uint6 moq->next_request_id = request_id + IMQUIC_MOQ_REQUEST_ID_INCREMENT; imquic_refcount_increase(&moq->ref); imquic_mutex_unlock(&moq_mutex); - /* TODO Check if this namespace exists and was publish_namespaced here */ - /* TODO Track subscription and track alias */ - if(moq->version >= IMQUIC_MOQ_VERSION_15) { - /* Map this request ID to this message type, so that we can trigger - * the right application callbac if/when we get a response later on */ + /* Map this request ID to this message type, so that we can trigger + * the right application callbac if/when we get a response later on */ + imquic_mutex_lock(&moq->mutex); + g_hash_table_insert(moq->requests, imquic_dup_uint64(request_id), GUINT_TO_POINTER(IMQUIC_MOQ_FETCH)); + imquic_mutex_unlock(&moq->mutex); + /* Starting from v17, requests on a dedicated bidirectional STREAM */ + imquic_moq_stream *moq_stream = NULL; + if(moq->version >= IMQUIC_MOQ_VERSION_17) { + moq_stream = imquic_moq_stream_create(); + imquic_connection_new_stream_id(moq->conn, TRUE, &moq_stream->stream_id); + moq_stream->request_type = IMQUIC_MOQ_FETCH; + moq_stream->request_id = request_id; + moq_stream->request_sender = TRUE; + moq_stream->request_state = IMQUIC_MOQ_REQUEST_STATE_SENT; imquic_mutex_lock(&moq->mutex); - g_hash_table_insert(moq->requests, imquic_dup_uint64(request_id), GUINT_TO_POINTER(IMQUIC_MOQ_FETCH)); + g_hash_table_insert(moq->streams, imquic_dup_uint64(moq_stream->stream_id), moq_stream); + g_hash_table_insert(moq->streams_by_reqid, imquic_dup_uint64(request_id), moq_stream); imquic_mutex_unlock(&moq->mutex); } uint8_t buffer[200]; size_t blen = sizeof(buffer); size_t f_len = 0; - f_len = imquic_moq_add_fetch(moq, buffer, blen, + f_len = imquic_moq_add_fetch(moq, moq_stream, buffer, blen, (absolute ? IMQUIC_MOQ_FETCH_JOINING_ABSOLUTE : IMQUIC_MOQ_FETCH_JOINING_RELATIVE), - request_id, joining_request_id, joining_start, + request_id, required_id_delta, joining_request_id, joining_start, NULL, NULL, /* Ignored, as namespaces/track are only used for Standalone Fetch */ NULL, /* Ignored, as the fetch range is only used for Standalone Fetch */ parameters); - imquic_connection_send_on_stream(conn, moq->control_stream_id, + imquic_connection_send_on_stream(conn, + moq_stream ? moq_stream->stream_id : moq->control_stream_id, buffer, f_len, FALSE); /* Done */ imquic_refcount_decrease(&moq->ref); @@ -7899,7 +6946,7 @@ int imquic_moq_joining_fetch(imquic_connection *conn, uint64_t request_id, uint6 } int imquic_moq_accept_fetch(imquic_connection *conn, uint64_t request_id, imquic_moq_location *largest, - imquic_moq_request_parameters *parameters, GList *track_extensions) { + imquic_moq_request_parameters *parameters, GList *track_properties) { imquic_mutex_lock(&moq_mutex); imquic_moq_context *moq = g_hash_table_lookup(moq_sessions, conn); if(moq == NULL || largest == NULL) { @@ -7908,29 +6955,35 @@ int imquic_moq_accept_fetch(imquic_connection *conn, uint64_t request_id, imquic imquic_mutex_unlock(&moq_mutex); return -1; } - if(moq->version <= IMQUIC_MOQ_VERSION_14 && parameters == NULL) { - IMQUIC_LOG(IMQUIC_LOG_ERR, "[%s][MoQ] Invalid arguments (missing mandatory parameters)\n", - imquic_get_connection_name(conn)); - imquic_mutex_unlock(&moq_mutex); - return -1; - } - if(moq->version <= IMQUIC_MOQ_VERSION_14 && !parameters->group_order_set) { - /* Force some defaults */ - parameters->group_order_set = TRUE; - parameters->group_order = IMQUIC_MOQ_ORDERING_ASCENDING; - } imquic_refcount_increase(&moq->ref); imquic_mutex_unlock(&moq_mutex); - /* TODO Check if we were fetched */ + /* Starting from v17, requests go on a dedicated bidirectional + * STREAM, and the same applies to the FETCH_OK responses */ + imquic_moq_stream *moq_stream = NULL; + if(moq->version >= IMQUIC_MOQ_VERSION_17) { + imquic_mutex_lock(&moq->mutex); + moq_stream = g_hash_table_lookup(moq->streams_by_reqid, &request_id); + if(moq_stream == NULL || moq_stream->request_type != IMQUIC_MOQ_FETCH || moq_stream->request_sender || + moq_stream->request_state != IMQUIC_MOQ_REQUEST_STATE_SENT) { + IMQUIC_LOG(IMQUIC_LOG_ERR, "[%s][MoQ] Invalid request/state (%s)\n", + imquic_get_connection_name(conn), moq_stream ? imquic_media_stream_request_state_str(moq_stream->request_state) : "No stream"); + imquic_mutex_unlock(&moq->mutex); + imquic_refcount_decrease(&moq->ref); + return -1; + } + moq_stream->request_state = IMQUIC_MOQ_REQUEST_STATE_OK; + imquic_mutex_unlock(&moq->mutex); + } uint8_t buffer[200]; size_t blen = sizeof(buffer); /* TODO Make other properties configurable */ - size_t f_len = imquic_moq_add_fetch_ok(moq, buffer, blen, + size_t f_len = imquic_moq_add_fetch_ok(moq, moq_stream, buffer, blen, request_id, 0, /* TODO End of track */ largest, /* Largest location */ - parameters, track_extensions); - imquic_connection_send_on_stream(conn, moq->control_stream_id, + parameters, track_properties); + imquic_connection_send_on_stream(conn, + moq_stream ? moq_stream->stream_id : moq->control_stream_id, buffer, f_len, FALSE); /* Done */ imquic_refcount_decrease(&moq->ref); @@ -7949,17 +7002,35 @@ int imquic_moq_reject_fetch(imquic_connection *conn, uint64_t request_id, } imquic_refcount_increase(&moq->ref); imquic_mutex_unlock(&moq_mutex); - /* TODO Check if we were fetched */ + /* Starting from v17, requests go on a dedicated bidirectional + * STREAM, and the same applies to the REQUEST_ERROR responses */ + imquic_moq_stream *moq_stream = NULL; + if(moq->version >= IMQUIC_MOQ_VERSION_17) { + imquic_mutex_lock(&moq->mutex); + moq_stream = g_hash_table_lookup(moq->streams_by_reqid, &request_id); + if(moq_stream == NULL || moq_stream->request_type == 0 || moq_stream->request_sender || + moq_stream->request_state != IMQUIC_MOQ_REQUEST_STATE_SENT) { + IMQUIC_LOG(IMQUIC_LOG_ERR, "[%s][MoQ] Invalid request/state (%s)\n", + imquic_get_connection_name(conn), moq_stream ? imquic_media_stream_request_state_str(moq_stream->request_state) : "No stream"); + imquic_mutex_unlock(&moq->mutex); + imquic_refcount_decrease(&moq->ref); + return -1; + } + moq_stream->request_state = IMQUIC_MOQ_REQUEST_STATE_ERROR; + imquic_mutex_unlock(&moq->mutex); + } uint8_t buffer[200]; size_t blen = sizeof(buffer); - size_t f_len = 0; - if(moq->version <= IMQUIC_MOQ_VERSION_14) { - f_len = imquic_moq_add_fetch_error(moq, buffer, blen, request_id, error_code, reason); - } else { - f_len = imquic_moq_add_request_error(moq, NULL, buffer, blen, request_id, error_code, reason, retry_interval); + size_t f_len = imquic_moq_add_request_error(moq, moq_stream, buffer, blen, request_id, error_code, reason, retry_interval); + imquic_connection_send_on_stream(conn, + moq_stream ? moq_stream->stream_id : moq->control_stream_id, + buffer, f_len, moq_stream ? TRUE : FALSE); + if(moq_stream != NULL) { + imquic_mutex_lock(&moq->mutex); + g_hash_table_remove(moq->streams_by_reqid, &moq_stream->request_id); + g_hash_table_remove(moq->streams, &moq_stream->stream_id); + imquic_mutex_unlock(&moq->mutex); } - imquic_connection_send_on_stream(conn, moq->control_stream_id, - buffer, f_len, FALSE); /* Done */ imquic_refcount_decrease(&moq->ref); return 0; @@ -7998,39 +7069,6 @@ int imquic_moq_track_status(imquic_connection *conn, uint64_t request_id, imquic_mutex_unlock(&moq_mutex); return -1; } - if(moq->version <= IMQUIC_MOQ_VERSION_14 && parameters == NULL) { - IMQUIC_LOG(IMQUIC_LOG_ERR, "[%s][MoQ] Invalid arguments (missing mandatory parameters)\n", - imquic_get_connection_name(conn)); - imquic_mutex_unlock(&moq_mutex); - return -1; - } - if(moq->version <= IMQUIC_MOQ_VERSION_14 && !parameters->forward_set) { - /* Force some defaults */ - parameters->forward_set = TRUE; - parameters->forward = TRUE; - } - if(moq->version <= IMQUIC_MOQ_VERSION_14 && !parameters->subscriber_priority_set) { - /* Force some defaults */ - parameters->subscriber_priority_set = TRUE; - parameters->subscriber_priority = 128; - } - if(moq->version <= IMQUIC_MOQ_VERSION_14 && !parameters->group_order_set) { - /* Force some defaults */ - parameters->group_order_set = TRUE; - parameters->group_order = IMQUIC_MOQ_ORDERING_ASCENDING; - } - if(moq->version <= IMQUIC_MOQ_VERSION_14 && !parameters->subscription_filter_set) { - /* Force some defaults */ - parameters->subscription_filter_set = TRUE; - parameters->subscription_filter.type = IMQUIC_MOQ_FILTER_LARGEST_OBJECT; - } - if(moq->version < IMQUIC_MOQ_VERSION_13) { - IMQUIC_LOG(IMQUIC_LOG_WARN, "[%s][MoQ] Can't send %s on a connection using %s\n", - imquic_get_connection_name(moq->conn), imquic_moq_message_type_str(IMQUIC_MOQ_TRACK_STATUS, moq->version), - imquic_moq_version_str(moq->version)); - imquic_mutex_unlock(&moq_mutex); - return -1; - } if(parameters && parameters->subscription_filter_set && parameters->subscription_filter.type == IMQUIC_MOQ_FILTER_ABSOLUTE_RANGE && parameters->subscription_filter.end_group > 0 && parameters->subscription_filter.start_location.group > parameters->subscription_filter.end_group) { IMQUIC_LOG(IMQUIC_LOG_ERR, "[%s][MoQ] End group is lower than start location group (%"SCNu64" < %"SCNu64")\n", @@ -8049,20 +7087,32 @@ int imquic_moq_track_status(imquic_connection *conn, uint64_t request_id, moq->next_request_id = request_id + IMQUIC_MOQ_REQUEST_ID_INCREMENT; imquic_refcount_increase(&moq->ref); imquic_mutex_unlock(&moq_mutex); - if(moq->version >= IMQUIC_MOQ_VERSION_15) { - /* Map this request ID to this message type, so that we can trigger - * the right application callbac if/when we get a response later on */ + /* Map this request ID to this message type, so that we can trigger + * the right application callbac if/when we get a response later on */ + imquic_mutex_lock(&moq->mutex); + g_hash_table_insert(moq->requests, imquic_dup_uint64(request_id), GUINT_TO_POINTER(IMQUIC_MOQ_TRACK_STATUS)); + imquic_mutex_unlock(&moq->mutex); + /* Starting from v17, requests on a dedicated bidirectional STREAM */ + imquic_moq_stream *moq_stream = NULL; + if(moq->version >= IMQUIC_MOQ_VERSION_17) { + moq_stream = imquic_moq_stream_create(); + imquic_connection_new_stream_id(moq->conn, TRUE, &moq_stream->stream_id); + moq_stream->request_type = IMQUIC_MOQ_TRACK_STATUS; + moq_stream->request_id = request_id; + moq_stream->request_sender = TRUE; + moq_stream->request_state = IMQUIC_MOQ_REQUEST_STATE_SENT; imquic_mutex_lock(&moq->mutex); - g_hash_table_insert(moq->requests, imquic_dup_uint64(request_id), GUINT_TO_POINTER(IMQUIC_MOQ_TRACK_STATUS)); + g_hash_table_insert(moq->streams, imquic_dup_uint64(moq_stream->stream_id), moq_stream); + g_hash_table_insert(moq->streams_by_reqid, imquic_dup_uint64(request_id), moq_stream); imquic_mutex_unlock(&moq->mutex); } - /* Send the request */ uint8_t buffer[200]; size_t blen = sizeof(buffer); size_t sb_len = 0; - sb_len = imquic_moq_add_track_status(moq, buffer, blen, + sb_len = imquic_moq_add_track_status(moq, moq_stream, buffer, blen, request_id, tns, tn, parameters); - imquic_connection_send_on_stream(conn, moq->control_stream_id, + imquic_connection_send_on_stream(conn, + moq_stream ? moq_stream->stream_id : moq->control_stream_id, buffer, sb_len, FALSE); /* Done */ imquic_refcount_decrease(&moq->ref); @@ -8070,7 +7120,7 @@ int imquic_moq_track_status(imquic_connection *conn, uint64_t request_id, } int imquic_moq_accept_track_status(imquic_connection *conn, uint64_t request_id, - uint64_t track_alias, imquic_moq_request_parameters *parameters) { + imquic_moq_request_parameters *parameters) { imquic_mutex_lock(&moq_mutex); imquic_moq_context *moq = g_hash_table_lookup(moq_sessions, conn); if(moq == NULL) { @@ -8079,47 +7129,30 @@ int imquic_moq_accept_track_status(imquic_connection *conn, uint64_t request_id, imquic_mutex_unlock(&moq_mutex); return -1; } - if(moq->version <= IMQUIC_MOQ_VERSION_14 && (parameters == NULL || !parameters->expires_set || - !parameters->group_order_set || !parameters->largest_object_set)) { - IMQUIC_LOG(IMQUIC_LOG_ERR, "[%s][MoQ] Invalid arguments (missing mandatory parameters)\n", - imquic_get_connection_name(conn)); - imquic_mutex_unlock(&moq_mutex); - return -1; - } - if(moq->version <= IMQUIC_MOQ_VERSION_14 && !parameters->expires_set) { - /* Force some defaults */ - parameters->expires_set = TRUE; - parameters->expires = 0; - } - if(moq->version <= IMQUIC_MOQ_VERSION_14 && !parameters->group_order_set) { - /* Force some defaults */ - parameters->group_order_set = TRUE; - parameters->group_order = IMQUIC_MOQ_ORDERING_ASCENDING; - } - if(moq->version <= IMQUIC_MOQ_VERSION_14 && !parameters->largest_object_set) { - /* Force some defaults */ - parameters->largest_object_set = TRUE; - parameters->largest_object.group = 0; - parameters->largest_object.object = 0; - } imquic_refcount_increase(&moq->ref); imquic_mutex_unlock(&moq_mutex); - if(moq->version < IMQUIC_MOQ_VERSION_13) { - IMQUIC_LOG(IMQUIC_LOG_WARN, "[%s][MoQ] Can't send %s on a connection using %s\n", - imquic_get_connection_name(moq->conn), imquic_moq_message_type_str(IMQUIC_MOQ_TRACK_STATUS_OK, moq->version), - imquic_moq_version_str(moq->version)); - return -1; + /* Starting from v17, requests go on a dedicated bidirectional + * STREAM, and the same applies to the REQUEST_OK responses */ + imquic_moq_stream *moq_stream = NULL; + if(moq->version >= IMQUIC_MOQ_VERSION_17) { + imquic_mutex_lock(&moq->mutex); + moq_stream = g_hash_table_lookup(moq->streams_by_reqid, &request_id); + if(moq_stream == NULL || moq_stream->request_type != IMQUIC_MOQ_TRACK_STATUS || moq_stream->request_sender || + moq_stream->request_state != IMQUIC_MOQ_REQUEST_STATE_SENT) { + IMQUIC_LOG(IMQUIC_LOG_ERR, "[%s][MoQ] Invalid request/state (%s)\n", + imquic_get_connection_name(conn), moq_stream ? imquic_media_stream_request_state_str(moq_stream->request_state) : "No stream"); + imquic_mutex_unlock(&moq->mutex); + imquic_refcount_decrease(&moq->ref); + return -1; + } + moq_stream->request_state = IMQUIC_MOQ_REQUEST_STATE_OK; + imquic_mutex_unlock(&moq->mutex); } uint8_t buffer[200]; size_t blen = sizeof(buffer); - size_t tso_len = 0; - if(moq->version <= IMQUIC_MOQ_VERSION_14) { - tso_len = imquic_moq_add_track_status_ok(moq, buffer, blen, - request_id, track_alias, parameters); - } else { - tso_len = imquic_moq_add_request_ok(moq, NULL, buffer, blen, request_id, parameters); - } - imquic_connection_send_on_stream(conn, moq->control_stream_id, + size_t tso_len = imquic_moq_add_request_ok(moq, moq_stream, buffer, blen, request_id, parameters); + imquic_connection_send_on_stream(conn, + moq_stream ? moq_stream->stream_id : moq->control_stream_id, buffer, tso_len, FALSE); /* Done */ imquic_refcount_decrease(&moq->ref); @@ -8138,22 +7171,34 @@ int imquic_moq_reject_track_status(imquic_connection *conn, uint64_t request_id, } imquic_refcount_increase(&moq->ref); imquic_mutex_unlock(&moq_mutex); - if(moq->version < IMQUIC_MOQ_VERSION_13) { - IMQUIC_LOG(IMQUIC_LOG_WARN, "[%s][MoQ] Can't send %s on a connection using %s\n", - imquic_get_connection_name(moq->conn), imquic_moq_message_type_str(IMQUIC_MOQ_TRACK_STATUS_ERROR, moq->version), - imquic_moq_version_str(moq->version)); - return -1; + /* Starting from v17, requests go on a dedicated bidirectional + * STREAM, and the same applies to the REQUEST_ERROR responses */ + imquic_moq_stream *moq_stream = NULL; + if(moq->version >= IMQUIC_MOQ_VERSION_17) { + imquic_mutex_lock(&moq->mutex); + moq_stream = g_hash_table_lookup(moq->streams_by_reqid, &request_id); + if(moq_stream == NULL || moq_stream->request_type != IMQUIC_MOQ_TRACK_STATUS || moq_stream->request_sender || + moq_stream->request_state != IMQUIC_MOQ_REQUEST_STATE_SENT) { + IMQUIC_LOG(IMQUIC_LOG_ERR, "[%s][MoQ] Invalid request/state (%s)\n", + imquic_get_connection_name(conn), moq_stream ? imquic_media_stream_request_state_str(moq_stream->request_state) : "No stream"); + imquic_mutex_unlock(&moq->mutex); + imquic_refcount_decrease(&moq->ref); + return -1; + } + moq_stream->request_state = IMQUIC_MOQ_REQUEST_STATE_ERROR; + imquic_mutex_unlock(&moq->mutex); } uint8_t buffer[200]; size_t blen = sizeof(buffer); - size_t tsr_len = 0; - if(moq->version <= IMQUIC_MOQ_VERSION_14) { - tsr_len = imquic_moq_add_track_status_error(moq, buffer, blen, request_id, error_code, reason); - } else { - tsr_len = imquic_moq_add_request_error(moq, NULL, buffer, blen, request_id, error_code, reason, retry_interval); - } + size_t tsr_len = imquic_moq_add_request_error(moq, NULL, buffer, blen, request_id, error_code, reason, retry_interval); imquic_connection_send_on_stream(conn, moq->control_stream_id, - buffer, tsr_len, FALSE); + buffer, tsr_len, moq_stream ? TRUE : FALSE); + if(moq_stream != NULL) { + imquic_mutex_lock(&moq->mutex); + g_hash_table_remove(moq->streams_by_reqid, &moq_stream->request_id); + g_hash_table_remove(moq->streams, &moq_stream->stream_id); + imquic_mutex_unlock(&moq->mutex); + } /* Done */ imquic_refcount_decrease(&moq->ref); return 0; @@ -8162,7 +7207,7 @@ int imquic_moq_reject_track_status(imquic_connection *conn, uint64_t request_id, int imquic_moq_requests_blocked(imquic_connection *conn) { imquic_mutex_lock(&moq_mutex); imquic_moq_context *moq = g_hash_table_lookup(moq_sessions, conn); - if(moq == NULL) { + if(moq == NULL || moq->version >= IMQUIC_MOQ_VERSION_17) { IMQUIC_LOG(IMQUIC_LOG_ERR, "[%s][MoQ] Invalid arguments\n", imquic_get_connection_name(conn)); imquic_mutex_unlock(&moq_mutex); @@ -8180,7 +7225,7 @@ int imquic_moq_requests_blocked(imquic_connection *conn) { return 0; } -int imquic_moq_goaway(imquic_connection *conn, const char *uri) { +int imquic_moq_goaway(imquic_connection *conn, const char *uri, uint64_t timeout) { imquic_mutex_lock(&moq_mutex); imquic_moq_context *moq = g_hash_table_lookup(moq_sessions, conn); if(moq == NULL) { @@ -8193,7 +7238,7 @@ int imquic_moq_goaway(imquic_connection *conn, const char *uri) { imquic_mutex_unlock(&moq_mutex); uint8_t buffer[200]; size_t blen = sizeof(buffer); - size_t g_len = imquic_moq_add_goaway(moq, buffer, blen, uri); + size_t g_len = imquic_moq_add_goaway(moq, buffer, blen, uri, timeout); imquic_connection_send_on_stream(conn, moq->control_stream_id, buffer, g_len, FALSE); /* Done */ @@ -8202,8 +7247,7 @@ int imquic_moq_goaway(imquic_connection *conn, const char *uri) { } int imquic_moq_send_object(imquic_connection *conn, imquic_moq_object *object) { - if(object == NULL || object->object_status > IMQUIC_MOQ_END_OF_TRACK || - (object->object_status == IMQUIC_MOQ_OBJECT_DOESNT_EXIST && object->extensions != NULL)) { + if(object == NULL || object->object_status > IMQUIC_MOQ_END_OF_TRACK) { IMQUIC_LOG(IMQUIC_LOG_ERR, "[%s][MoQ] Invalid arguments\n", imquic_get_connection_name(conn)); return -1; @@ -8221,13 +7265,13 @@ int imquic_moq_send_object(imquic_connection *conn, imquic_moq_object *object) { /* Check if we have data to send */ gboolean has_payload = (object->payload_len > 0 && object->payload != NULL); gboolean valid_pkt = has_payload || (object->object_status != IMQUIC_MOQ_NORMAL_OBJECT); - /* Check if there are extensions to encode */ - uint8_t extensions[256]; - size_t extensions_len = 0; - if(object->extensions != NULL) - extensions_len = imquic_moq_build_object_extensions(moq->version, object->extensions, extensions, sizeof(extensions)); + /* Check if there are properties to encode */ + uint8_t properties[256]; + size_t properties_len = 0; + if(object->properties != NULL) + properties_len = imquic_moq_build_properties(moq->version, object->properties, properties, sizeof(properties)); /* Check how we should send this */ - size_t bufsize = extensions_len + object->payload_len + 100; + size_t bufsize = properties_len + object->payload_len + 100; uint8_t *buffer = g_malloc(bufsize); /* FIXME */ if(object->delivery == IMQUIC_MOQ_USE_DATAGRAM) { /* Use a datagram */ @@ -8235,7 +7279,7 @@ int imquic_moq_send_object(imquic_connection *conn, imquic_moq_object *object) { size_t dg_len = imquic_moq_add_object_datagram(moq, buffer, bufsize, object->request_id, object->track_alias, object->group_id, object->object_id, object->object_status, object->priority, object->payload, object->payload_len, - extensions, extensions_len); + properties, properties_len); #ifdef HAVE_QLOG if(conn->qlog != NULL && conn->qlog->moq) imquic_moq_qlog_object_datagram_created(conn->qlog, object); @@ -8244,7 +7288,7 @@ int imquic_moq_send_object(imquic_connection *conn, imquic_moq_object *object) { } else { size_t dg_len = imquic_moq_add_object_datagram_status(moq, buffer, bufsize, object->track_alias, object->group_id, object->object_id, object->priority, - object->object_status, extensions, extensions_len); + object->object_status, properties, properties_len); imquic_connection_send_on_datagram(conn, buffer, dg_len); #ifdef HAVE_QLOG if(conn->qlog != NULL && conn->qlog->moq) @@ -8275,15 +7319,15 @@ int imquic_moq_send_object(imquic_connection *conn, imquic_moq_object *object) { return -1; } /* Create a new stream */ - moq_stream = g_malloc0(sizeof(imquic_moq_stream)); - /* TODO Change the type depending on whether extensions/subgroup will be set: + moq_stream = imquic_moq_stream_create(); + /* TODO Change the type depending on whether properties/subgroup will be set: * since we don't have an API for that, for now we always set the type * that will allow us to dynamically use them all. This also means we * currently don't have a way to specify an End-of-Group flag */ moq_stream->type = imquic_moq_data_message_type_from_subgroup_header(moq->version, TRUE, /* We'll explicitly specify the Subgroup ID */ FALSE, /* Whether the default Subgroup ID is 0 (ignored, since we set it) */ - TRUE, /* We'll add the extensions block, whether there are extensions or not */ + TRUE, /* We'll add the properties block, whether there are properties or not */ TRUE, /* End-of-Group is set */ TRUE); /* We'll add the Publisher Priority property */ moq_stream->priority = 128; /* FIXME */ @@ -8311,24 +7355,22 @@ int imquic_moq_send_object(imquic_connection *conn, imquic_moq_object *object) { size_t shgo_len = 0; if(valid_pkt) { uint64_t object_id = object->object_id; - if(moq->version >= IMQUIC_MOQ_VERSION_14) { - /* Object IDs are a delta, starting from v14 */ - if(moq_stream->got_objects && object_id <= moq_stream->last_object_id) { - IMQUIC_LOG(IMQUIC_LOG_ERR, "[%s][MoQ] Can't send older object on this subgroup (%"SCNu64" <= %"SCNu64")\n", - imquic_get_connection_name(conn), object_id, moq_stream->last_object_id); - imquic_refcount_decrease(&moq->ref); - g_free(buffer); - return -1; - } - object_id -= moq_stream->last_object_id; - if(moq_stream->got_objects) - object_id--; - moq_stream->got_objects = TRUE; - moq_stream->last_object_id = object->object_id; + /* Object IDs are a delta */ + if(moq_stream->got_objects && object_id <= moq_stream->last_object_id) { + IMQUIC_LOG(IMQUIC_LOG_ERR, "[%s][MoQ] Can't send older object on this subgroup (%"SCNu64" <= %"SCNu64")\n", + imquic_get_connection_name(conn), object_id, moq_stream->last_object_id); + imquic_refcount_decrease(&moq->ref); + g_free(buffer); + return -1; } + object_id -= moq_stream->last_object_id; + if(moq_stream->got_objects) + object_id--; + moq_stream->got_objects = TRUE; + moq_stream->last_object_id = object->object_id; shgo_len = imquic_moq_add_subgroup_header_object(moq, moq_stream, buffer, bufsize, object_id, object->object_status, object->payload, object->payload_len, - extensions, extensions_len); + properties, properties_len); #ifdef HAVE_QLOG if(conn->qlog != NULL && conn->qlog->moq) imquic_moq_qlog_subgroup_object_created(conn->qlog, moq_stream->stream_id, object); @@ -8364,7 +7406,7 @@ int imquic_moq_send_object(imquic_connection *conn, imquic_moq_object *object) { return -1; } /* Create a new stream */ - moq_stream = g_malloc0(sizeof(imquic_moq_stream)); + moq_stream = imquic_moq_stream_create(); moq_stream->type = IMQUIC_MOQ_FETCH_HEADER; moq_stream->priority = 128; /* FIXME */ imquic_connection_new_stream_id(conn, FALSE, &moq_stream->stream_id); @@ -8395,13 +7437,13 @@ int imquic_moq_send_object(imquic_connection *conn, imquic_moq_object *object) { TRUE, /* We write the Object ID */ TRUE, /* We write the Group ID */ TRUE, /* We write the Priority */ - TRUE, /* We add extensions */ + TRUE, /* We add properties */ FALSE, /* We assume Forwarding Preference is not DATAGRAM */ FALSE, FALSE); /* We don't use the "end of range" flags */ shto_len = imquic_moq_add_fetch_header_object(moq, buffer, bufsize, flags, object->group_id, object->subgroup_id, object->object_id, object->priority, object->object_status, object->payload, object->payload_len, - extensions, extensions_len); + properties, properties_len); #ifdef HAVE_QLOG if(conn->qlog != NULL && conn->qlog->moq) imquic_moq_qlog_fetch_object_created(conn->qlog, moq_stream->stream_id, object); @@ -8422,6 +7464,158 @@ int imquic_moq_send_object(imquic_connection *conn, imquic_moq_object *object) { return 0; } +/* Reading and writing MoQ's flavour of variable size integers */ +static uint64_t imquic_read_moqint(imquic_moq_version version, uint8_t *bytes, size_t blen, uint8_t *length) { + if(version <= IMQUIC_MOQ_VERSION_16) + return imquic_read_varint(bytes, blen, length); + if(length) + *length = 0; + if(bytes == NULL || blen == 0) + return 0; + if(bytes[0] == 0xFC) { + IMQUIC_LOG(IMQUIC_LOG_ERR, "Invalid moqint code point\n"); + return 0; + } + /* Check how many bytes we need */ + uint8_t len = 0; + uint64_t res = 0; + if((bytes[0] >> 7) == 0) { + len = 1; + res = bytes[0]; + goto done; + } else if((bytes[0] >> 6) == 0x02) { + len = 2; + } else if((bytes[0] >> 5) == 0x06) { + len = 3; + } else if((bytes[0] >> 4) == 0x0E) { + len = 4; + } else if((bytes[0] >> 3) == 0x1E) { + len = 5; + } else if((bytes[0] >> 2) == 0x3E) { + len = 6; + } else if((bytes[0]) == 0xFE) { + len = 8; + } else if((bytes[0]) == 0xFF) { + len = 9; + } + if(len == 0 || len > blen) { + IMQUIC_LOG(IMQUIC_LOG_ERR, "Invalid moqint (%"SCNu8" > %zu)\n", len, blen); + return 0; + } + if(len < 8) { + uint8_t temp = bytes[0] << len; + res = temp >> len; + } + for(uint8_t i=1; ipath_set) { + json_t *opts = json_array(); + if(options->path_set) { json_t *path = json_object(); json_object_set_new(path, "name", json_string("path")); - json_object_set_new(path, "value", json_string(parameters->path)); - json_array_append_new(params, path); + json_object_set_new(path, "value", json_string(options->path)); + json_array_append_new(opts, path); } - if(parameters->max_request_id_set) { + if(options->max_request_id_set) { json_t *max_request_id = json_object(); json_object_set_new(max_request_id, "name", json_string("max_request_id")); - json_object_set_new(max_request_id, "value", json_integer(parameters->max_request_id)); - json_array_append_new(params, max_request_id); + json_object_set_new(max_request_id, "value", json_integer(options->max_request_id)); + json_array_append_new(opts, max_request_id); } - if(parameters->max_auth_token_cache_size_set) { + if(options->max_auth_token_cache_size_set) { json_t *max_auth_token_cache_size = json_object(); json_object_set_new(max_auth_token_cache_size, "name", json_string("max_auth_token_cache_size")); - json_object_set_new(max_auth_token_cache_size, "value", json_integer(parameters->max_auth_token_cache_size)); - json_array_append_new(params, max_auth_token_cache_size); + json_object_set_new(max_auth_token_cache_size, "value", json_integer(options->max_auth_token_cache_size)); + json_array_append_new(opts, max_auth_token_cache_size); } - if(parameters->auth_token_set && parameters->auth_token_len > 0) { + if(options->auth_token_set && options->auth_token_len > 0) { json_t *auth_token = json_object(); json_object_set_new(auth_token, "name", json_string("authorization_token")); char ai_str[513]; - json_object_set_new(auth_token, "value", json_string(imquic_hex_str(parameters->auth_token, parameters->auth_token_len, ai_str, sizeof(ai_str)))); - json_array_append_new(params, auth_token); + json_object_set_new(auth_token, "value", json_string(imquic_hex_str(options->auth_token, options->auth_token_len, ai_str, sizeof(ai_str)))); + json_array_append_new(opts, auth_token); } - if(parameters->authority_set) { + if(options->authority_set) { json_t *authority = json_object(); json_object_set_new(authority, "name", json_string("authority")); - json_object_set_new(authority, "value", json_string(parameters->authority)); - json_array_append_new(params, authority); + json_object_set_new(authority, "value", json_string(options->authority)); + json_array_append_new(opts, authority); } - if(parameters->moqt_implementation_set) { + if(options->moqt_implementation_set) { json_t *moqt_implementation = json_object(); json_object_set_new(moqt_implementation, "name", json_string("moqt_implementation")); - json_object_set_new(moqt_implementation, "value", json_string(parameters->moqt_implementation)); - json_array_append_new(params, moqt_implementation); + json_object_set_new(moqt_implementation, "value", json_string(options->moqt_implementation)); + json_array_append_new(opts, moqt_implementation); } - if(parameters->unknown) { + if(options->unknown) { json_t *unknown = json_object(); json_object_set_new(unknown, "name", json_string("unknown")); - json_array_append_new(params, unknown); + json_array_append_new(opts, unknown); } - json_object_set_new(message, name, params); + json_object_set_new(message, name, opts); } void imquic_qlog_moq_message_add_request_parameters(json_t *message, imquic_moq_version version, imquic_moq_request_parameters *parameters, const char *name) { @@ -8525,31 +7719,25 @@ void imquic_qlog_moq_message_add_request_parameters(json_t *message, imquic_moq_ json_object_set_new(delivery_timeout, "value", json_integer(parameters->delivery_timeout)); json_array_append_new(params, delivery_timeout); } - if(parameters->max_cache_duration_set) { - json_t *max_cache_duration = json_object(); - json_object_set_new(max_cache_duration, "name", json_string("max_cache_duration")); - json_object_set_new(max_cache_duration, "value", json_integer(parameters->max_cache_duration)); - json_array_append_new(params, max_cache_duration); - } - if(version >= IMQUIC_MOQ_VERSION_15 && parameters->publisher_priority_set) { - json_t *publisher_priority = json_object(); - json_object_set_new(publisher_priority, "name", json_string("publisher_priority")); - json_object_set_new(publisher_priority, "value", json_integer(parameters->publisher_priority)); - json_array_append_new(params, publisher_priority); + if(parameters->rendezvous_timeout_set) { + json_t *rendezvous_timeout = json_object(); + json_object_set_new(rendezvous_timeout, "name", json_string("rendezvous_timeout")); + json_object_set_new(rendezvous_timeout, "value", json_integer(parameters->rendezvous_timeout)); + json_array_append_new(params, rendezvous_timeout); } - if(version >= IMQUIC_MOQ_VERSION_15 && parameters->subscriber_priority_set) { + if(parameters->subscriber_priority_set) { json_t *subscriber_priority = json_object(); json_object_set_new(subscriber_priority, "name", json_string("subscriber_priority")); json_object_set_new(subscriber_priority, "value", json_integer(parameters->subscriber_priority)); json_array_append_new(params, subscriber_priority); } - if(version >= IMQUIC_MOQ_VERSION_15 && parameters->group_order_set) { + if(parameters->group_order_set) { json_t *group_order = json_object(); json_object_set_new(group_order, "name", json_string("group_order")); json_object_set_new(group_order, "value", json_integer(parameters->group_order)); json_array_append_new(params, group_order); } - if(version >= IMQUIC_MOQ_VERSION_15 && parameters->subscription_filter_set) { + if(parameters->subscription_filter_set) { json_t *subscription_filter = json_object(); json_object_set_new(subscription_filter, "name", json_string("subscription_filter")); /* FIXME */ @@ -8562,18 +7750,22 @@ void imquic_qlog_moq_message_add_request_parameters(json_t *message, imquic_moq_ json_object_set_new(lo, "object", json_integer(parameters->subscription_filter.start_location.object)); json_object_set_new(sf, "start_location", lo); } - if(parameters->subscription_filter.type == IMQUIC_MOQ_FILTER_ABSOLUTE_RANGE) - json_object_set_new(sf, "end_group", json_integer(parameters->subscription_filter.end_group)); + if(parameters->subscription_filter.type == IMQUIC_MOQ_FILTER_ABSOLUTE_RANGE) { + if(version <= IMQUIC_MOQ_VERSION_16) + json_object_set_new(sf, "end_group", json_integer(parameters->subscription_filter.end_group)); + else + json_object_set_new(sf, "end_group_delta", json_integer(parameters->subscription_filter.end_group - parameters->subscription_filter.start_location.group)); + } json_object_set_new(subscription_filter, "value", sf); json_array_append_new(params, subscription_filter); } - if(version >= IMQUIC_MOQ_VERSION_15 && parameters->expires_set) { + if(parameters->expires_set) { json_t *expires = json_object(); json_object_set_new(expires, "name", json_string("expires")); json_object_set_new(expires, "value", json_integer(parameters->expires)); json_array_append_new(params, expires); } - if(version >= IMQUIC_MOQ_VERSION_15 && parameters->largest_object_set) { + if(parameters->largest_object_set) { json_t *largest_object = json_object(); json_object_set_new(largest_object, "name", json_string("largest_object")); /* FIXME */ @@ -8583,19 +7775,13 @@ void imquic_qlog_moq_message_add_request_parameters(json_t *message, imquic_moq_ json_object_set_new(largest_object, "value", lo); json_array_append_new(params, largest_object); } - if(version >= IMQUIC_MOQ_VERSION_15 && parameters->forward_set) { + if(parameters->forward_set) { json_t *forward = json_object(); json_object_set_new(forward, "name", json_string("forward")); json_object_set_new(forward, "value", json_integer(parameters->forward)); json_array_append_new(params, forward); } - if(version >= IMQUIC_MOQ_VERSION_15 && parameters->dynamic_groups_set) { - json_t *dynamic_groups = json_object(); - json_object_set_new(dynamic_groups, "name", json_string("dynamic_groups")); - json_object_set_new(dynamic_groups, "value", json_integer(parameters->dynamic_groups)); - json_array_append_new(params, dynamic_groups); - } - if(version >= IMQUIC_MOQ_VERSION_15 && parameters->new_group_request_set) { + if(parameters->new_group_request_set) { json_t *new_group_request = json_object(); json_object_set_new(new_group_request, "name", json_string("new_group_request")); json_object_set_new(new_group_request, "value", json_integer(parameters->new_group_request)); @@ -8609,20 +7795,20 @@ void imquic_qlog_moq_message_add_request_parameters(json_t *message, imquic_moq_ json_object_set_new(message, name, params); } -void imquic_qlog_moq_message_add_extensions(json_t *message, GList *extensions, const char *name) { - if(message == NULL || extensions == NULL || name == NULL) +void imquic_qlog_moq_message_add_properties(json_t *message, GList *properties, const char *name) { + if(message == NULL || properties == NULL || name == NULL) return; json_t *headers = json_array(); - GList *temp = extensions; + GList *temp = properties; while(temp) { - imquic_moq_object_extension *ext = (imquic_moq_object_extension *)temp->data; + imquic_moq_property *prop = (imquic_moq_property *)temp->data; json_t *header = json_object(); - json_object_set_new(header, "header_type", json_integer(ext->id)); - if(ext->id % 2 == 0) { - json_object_set_new(header, "header_value", json_integer(ext->value.number)); + json_object_set_new(header, "type", json_integer(prop->id)); + if(prop->id % 2 == 0) { + json_object_set_new(header, "value", json_integer(prop->value.number)); } else { /* FIXME */ - json_object_set_new(header, "header_length", json_integer(ext->value.data.length)); + json_object_set_new(header, "length", json_integer(prop->value.data.length)); } json_array_append_new(headers, header); temp = temp->next; @@ -8684,7 +7870,7 @@ void imquic_moq_qlog_object_datagram_created(imquic_qlog *qlog, imquic_moq_objec json_object_set_new(data, "group_id", json_integer(object->group_id)); json_object_set_new(data, "object_id", json_integer(object->object_id)); json_object_set_new(data, "publisher_priority", json_integer(object->priority)); - imquic_qlog_moq_message_add_extensions(data, object->extensions, "extension_headers"); + imquic_qlog_moq_message_add_properties(data, object->properties, "properties"); if(object->payload_len > 0) { imquic_qlog_event_add_raw(data, "object_payload", (qlog->moq_objects ? object->payload : NULL), object->payload_len); @@ -8701,7 +7887,7 @@ void imquic_moq_qlog_object_datagram_parsed(imquic_qlog *qlog, imquic_moq_object json_object_set_new(data, "group_id", json_integer(object->group_id)); json_object_set_new(data, "object_id", json_integer(object->object_id)); json_object_set_new(data, "publisher_priority", json_integer(object->priority)); - imquic_qlog_moq_message_add_extensions(data, object->extensions, "extension_headers"); + imquic_qlog_moq_message_add_properties(data, object->properties, "properties"); if(object->payload_len > 0) { imquic_qlog_event_add_raw(data, "object_payload", (qlog->moq_objects ? object->payload : NULL), object->payload_len); @@ -8718,7 +7904,7 @@ void imquic_moq_qlog_object_datagram_status_created(imquic_qlog *qlog, imquic_mo json_object_set_new(data, "group_id", json_integer(object->group_id)); json_object_set_new(data, "object_id", json_integer(object->object_id)); json_object_set_new(data, "publisher_priority", json_integer(object->priority)); - imquic_qlog_moq_message_add_extensions(data, object->extensions, "extension_headers"); + imquic_qlog_moq_message_add_properties(data, object->properties, "properties"); json_object_set_new(data, "object_status", json_integer(object->object_status)); if(object->payload_len > 0) { imquic_qlog_event_add_raw(data, "object_payload", @@ -8736,7 +7922,7 @@ void imquic_moq_qlog_object_datagram_status_parsed(imquic_qlog *qlog, imquic_moq json_object_set_new(data, "group_id", json_integer(object->group_id)); json_object_set_new(data, "object_id", json_integer(object->object_id)); json_object_set_new(data, "publisher_priority", json_integer(object->priority)); - imquic_qlog_moq_message_add_extensions(data, object->extensions, "extension_headers"); + imquic_qlog_moq_message_add_properties(data, object->properties, "properties"); json_object_set_new(data, "object_status", json_integer(object->object_status)); if(object->payload_len > 0) { imquic_qlog_event_add_raw(data, "object_payload", @@ -8789,7 +7975,7 @@ void imquic_moq_qlog_subgroup_object_created(imquic_qlog *qlog, uint64_t stream_ json_object_set_new(data, "subgroup_id", json_integer(object->group_id)); json_object_set_new(data, "object_id", json_integer(object->object_id)); json_object_set_new(data, "publisher_priority", json_integer(object->priority)); - imquic_qlog_moq_message_add_extensions(data, object->extensions, "extension_headers"); + imquic_qlog_moq_message_add_properties(data, object->properties, "properties"); json_object_set_new(data, "object_payload_length", json_integer(object->payload_len)); json_object_set_new(data, "object_status", json_integer(object->object_status)); if(object->payload_len > 0) { @@ -8809,7 +7995,7 @@ void imquic_moq_qlog_subgroup_object_parsed(imquic_qlog *qlog, uint64_t stream_i json_object_set_new(data, "subgroup_id", json_integer(object->group_id)); json_object_set_new(data, "object_id", json_integer(object->object_id)); json_object_set_new(data, "publisher_priority", json_integer(object->priority)); - imquic_qlog_moq_message_add_extensions(data, object->extensions, "extension_headers"); + imquic_qlog_moq_message_add_properties(data, object->properties, "properties"); json_object_set_new(data, "object_payload_length", json_integer(object->payload_len)); json_object_set_new(data, "object_status", json_integer(object->object_status)); if(object->payload_len > 0) { @@ -8854,7 +8040,7 @@ void imquic_moq_qlog_fetch_object_created(imquic_qlog *qlog, uint64_t stream_id, json_object_set_new(data, "group_id", json_integer(object->group_id)); json_object_set_new(data, "subgroup_id", json_integer(object->group_id)); json_object_set_new(data, "object_id", json_integer(object->object_id)); - imquic_qlog_moq_message_add_extensions(data, object->extensions, "extension_headers"); + imquic_qlog_moq_message_add_properties(data, object->properties, "properties"); json_object_set_new(data, "object_payload_length", json_integer(object->payload_len)); json_object_set_new(data, "object_status", json_integer(object->object_status)); if(object->payload_len > 0) { @@ -8873,7 +8059,7 @@ void imquic_moq_qlog_fetch_object_parsed(imquic_qlog *qlog, uint64_t stream_id, json_object_set_new(data, "group_id", json_integer(object->group_id)); json_object_set_new(data, "subgroup_id", json_integer(object->group_id)); json_object_set_new(data, "object_id", json_integer(object->object_id)); - imquic_qlog_moq_message_add_extensions(data, object->extensions, "extension_headers"); + imquic_qlog_moq_message_add_properties(data, object->properties, "properties"); json_object_set_new(data, "object_payload_length", json_integer(object->payload_len)); json_object_set_new(data, "object_status", json_integer(object->object_status)); if(object->payload_len > 0) { diff --git a/src/quic.c b/src/quic.c index 0ad88e0..868f312 100644 --- a/src/quic.c +++ b/src/quic.c @@ -10,6 +10,7 @@ #include #include +#include #include #include "internal/quic.h" @@ -206,6 +207,13 @@ gboolean imquic_quic_queued_event(imquic_connection *conn, imquic_connection_eve IMQUIC_LOG(IMQUIC_LOG_WARN, "[%s] Error resetting STREAM %"SCNu64": %d\n", conn->name, event->stream_id, ret); } + } else if(event->type == IMQUIC_CONNECTION_EVENT_STOP_SENDING) { + /* Send a STOP_SENDING */ + int ret = picoquic_stop_sending(conn->piconn, event->stream_id, event->error_code); + if(ret != 0) { + IMQUIC_LOG(IMQUIC_LOG_WARN, "[%s] Error stopping STREAM %"SCNu64": %d\n", + conn->name, event->stream_id, ret); + } } else if(event->type == IMQUIC_CONNECTION_EVENT_CLOSE_CONN) { /* Send a CONNECTION_CLOSE */ int ret = picoquic_close(conn->piconn, event->error_code); @@ -231,7 +239,7 @@ void imquic_quic_next_step(imquic_network_endpoint *endpoint) { static int imquic_quic_stream_callback(picoquic_cnx_t *pconn, uint64_t stream_id, uint8_t *bytes, size_t blen, picoquic_call_back_event_t fin_or_event, void *callback_ctx, void *v_stream_ctx) { - /* TODO */ + /* Check what the callback is about */ imquic_network_endpoint *endpoint = (imquic_network_endpoint *)callback_ctx; imquic_connection *conn = pconn ? g_hash_table_lookup(endpoint->connections_by_cnx, pconn) : NULL; char *name = conn ? conn->name : endpoint->name; @@ -319,6 +327,42 @@ static int imquic_quic_stream_callback(picoquic_cnx_t *pconn, /* Pass the data to the application callback */ imquic_connection_notify_stream_incoming(conn, stream, bytes, blen); } + } else if(fin_or_event == picoquic_callback_stream_reset) { + /* Use the picoquic internal API to obtain the error_code */ + uint64_t error_code = 0; + picoquic_stream_head_t *ps = picoquic_find_stream(pconn, stream_id); + if(ps != NULL) + error_code = ps->remote_error; + /* Update the local state of the stream */ + imquic_mutex_lock(&conn->mutex); + imquic_stream *stream = g_hash_table_lookup(conn->streams, &stream_id); + if(stream != NULL) { + IMQUIC_LOG(IMQUIC_LOG_INFO, "Stream %"SCNu64" has been reset by the peer\n", stream_id); + if(stream->in_state != IMQUIC_STREAM_COMPLETE) + stream->in_state = IMQUIC_STREAM_RESET; + } + imquic_mutex_unlock(&conn->mutex); + /* Pass the data to the application callback */ + if(endpoint->reset_stream_incoming) + endpoint->reset_stream_incoming(conn, stream_id, error_code); + } else if(fin_or_event == picoquic_callback_stop_sending) { + /* Use the picoquic internal API to obtain the error_code */ + uint64_t error_code = 0; + picoquic_stream_head_t *ps = picoquic_find_stream(pconn, stream_id); + if(ps != NULL) + error_code = ps->remote_stop_error; + /* Update the local state of the stream */ + imquic_mutex_lock(&conn->mutex); + imquic_stream *stream = g_hash_table_lookup(conn->streams, &stream_id); + if(stream != NULL) { + IMQUIC_LOG(IMQUIC_LOG_INFO, "We've been asked to stop sending on stream %"SCNu64"\n", stream_id); + if(stream->out_state != IMQUIC_STREAM_COMPLETE) + stream->out_state = IMQUIC_STREAM_RESET; + } + imquic_mutex_unlock(&conn->mutex); + /* Pass the data to the application callback */ + if(endpoint->stop_sending_incoming) + endpoint->stop_sending_incoming(conn, stream_id, error_code); } else if(fin_or_event == picoquic_callback_application_close) { /* TODO Should we handle this somehow? */ } else if(fin_or_event == picoquic_callback_close) {