Script 'mail_helper' called by obssrc Hello community, here is the log from the commit of package aws-c-mqtt for openSUSE:Factory checked in at 2024-06-03 17:41:17 ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ Comparing /work/SRC/openSUSE:Factory/aws-c-mqtt (Old) and /work/SRC/openSUSE:Factory/.aws-c-mqtt.new.24587 (New) ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ Package is "aws-c-mqtt" Mon Jun 3 17:41:17 2024 rev:4 rq:1177910 version:0.10.4 Changes: -------- --- /work/SRC/openSUSE:Factory/aws-c-mqtt/aws-c-mqtt.changes 2024-05-22 21:32:24.333822366 +0200 +++ /work/SRC/openSUSE:Factory/.aws-c-mqtt.new.24587/aws-c-mqtt.changes 2024-06-03 17:41:26.746543659 +0200 @@ -1,0 +2,7 @@ +Fri May 31 10:44:17 UTC 2024 - John Paul Adrian Glaubitz <adrian.glaubitz@suse.com> + +- Update to version 0.10.4 + * Refactor MQTT5 ping timeout by @bretambrose in (#361) + * Disabled keep alive by @bretambrose in (#363) + +------------------------------------------------------------------- Old: ---- v0.10.3.tar.gz New: ---- v0.10.4.tar.gz ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ Other differences: ------------------ ++++++ aws-c-mqtt.spec ++++++ --- /var/tmp/diff_new_pack.DcLekm/_old 2024-06-03 17:41:27.354565968 +0200 +++ /var/tmp/diff_new_pack.DcLekm/_new 2024-06-03 17:41:27.358566114 +0200 @@ -18,7 +18,7 @@ %global library_version 1_0_0 Name: aws-c-mqtt -Version: 0.10.3 +Version: 0.10.4 Release: 0 Summary: AWS C99 implementation of the MQTT 3.1.1 specification License: Apache-2.0 ++++++ v0.10.3.tar.gz -> v0.10.4.tar.gz ++++++ diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/aws-c-mqtt-0.10.3/include/aws/mqtt/private/v5/mqtt5_options_storage.h new/aws-c-mqtt-0.10.4/include/aws/mqtt/private/v5/mqtt5_options_storage.h --- old/aws-c-mqtt-0.10.3/include/aws/mqtt/private/v5/mqtt5_options_storage.h 2024-02-29 17:06:37.000000000 +0100 +++ new/aws-c-mqtt-0.10.4/include/aws/mqtt/private/v5/mqtt5_options_storage.h 2024-03-26 23:12:31.000000000 +0100 @@ -337,10 +337,6 @@ const struct aws_mqtt5_client_options_storage *options_storage, enum aws_log_level level); -AWS_MQTT_API bool aws_mqtt5_client_keep_alive_options_are_valid( - uint16_t keep_alive_interval_seconds, - uint32_t ping_timeout_ms); - AWS_EXTERN_C_END #endif /* AWS_MQTT_MQTT5_OPERATION_H */ diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/aws-c-mqtt-0.10.3/source/v5/mqtt5_client.c new/aws-c-mqtt-0.10.4/source/v5/mqtt5_client.c --- old/aws-c-mqtt-0.10.3/source/v5/mqtt5_client.c 2024-02-29 17:06:37.000000000 +0100 +++ new/aws-c-mqtt-0.10.4/source/v5/mqtt5_client.c 2024-03-26 23:12:31.000000000 +0100 @@ -1069,7 +1069,11 @@ uint64_t keep_alive_interval_nanos = aws_timestamp_convert(keep_alive_seconds, AWS_TIMESTAMP_SECS, AWS_TIMESTAMP_NANOS, NULL); - client->next_ping_time = aws_add_u64_saturating(now, keep_alive_interval_nanos); + if (keep_alive_interval_nanos == 0) { + client->next_ping_time = UINT64_MAX; + } else { + client->next_ping_time = aws_add_u64_saturating(now, keep_alive_interval_nanos); + } AWS_LOGF_DEBUG( AWS_LS_MQTT5_CLIENT, "id=%p: next PINGREQ scheduled for time %" PRIu64, (void *)client, client->next_ping_time); @@ -2940,7 +2944,20 @@ uint64_t now = client->vtable->get_current_time_fn(); uint64_t ping_timeout_nanos = aws_timestamp_convert(client->config->ping_timeout_ms, AWS_TIMESTAMP_MILLIS, AWS_TIMESTAMP_NANOS, NULL); - client->next_ping_timeout_time = aws_add_u64_saturating(now, ping_timeout_nanos); + uint64_t half_keep_alive_nanos = + aws_timestamp_convert( + client->negotiated_settings.server_keep_alive, AWS_TIMESTAMP_SECS, AWS_TIMESTAMP_NANOS, NULL) / + 2; + + uint64_t connection_ping_timeout = ping_timeout_nanos; + if (connection_ping_timeout == 0 || connection_ping_timeout > half_keep_alive_nanos) { + connection_ping_timeout = half_keep_alive_nanos; + } + + AWS_LOGF_DEBUG( + AWS_LS_MQTT5_CLIENT, "id=%p: dynamic ping timeout: %" PRIu64 " ns", (void *)client, connection_ping_timeout); + + client->next_ping_timeout_time = aws_add_u64_saturating(now, connection_ping_timeout); } static int s_apply_throughput_flow_control(struct aws_mqtt5_client *client) { diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/aws-c-mqtt-0.10.3/source/v5/mqtt5_options_storage.c new/aws-c-mqtt-0.10.4/source/v5/mqtt5_options_storage.c --- old/aws-c-mqtt-0.10.3/source/v5/mqtt5_options_storage.c 2024-02-29 17:06:37.000000000 +0100 +++ new/aws-c-mqtt-0.10.4/source/v5/mqtt5_options_storage.c 2024-03-26 23:12:31.000000000 +0100 @@ -3308,26 +3308,6 @@ return pingreq_op; } -bool aws_mqtt5_client_keep_alive_options_are_valid(uint16_t keep_alive_interval_seconds, uint32_t ping_timeout_ms) { - /* The client will not behave properly if ping timeout is not significantly shorter than the keep alive interval */ - if (keep_alive_interval_seconds > 0) { - uint64_t keep_alive_ms = - aws_timestamp_convert(keep_alive_interval_seconds, AWS_TIMESTAMP_SECS, AWS_TIMESTAMP_MILLIS, NULL); - uint64_t one_second_ms = aws_timestamp_convert(1, AWS_TIMESTAMP_SECS, AWS_TIMESTAMP_MILLIS, NULL); - - if (ping_timeout_ms == 0) { - ping_timeout_ms = AWS_MQTT5_CLIENT_DEFAULT_PING_TIMEOUT_MS; - } - - if ((uint64_t)ping_timeout_ms + one_second_ms > keep_alive_ms) { - AWS_LOGF_ERROR(AWS_LS_MQTT5_GENERAL, "keep alive interval is too small relative to ping timeout interval"); - return false; - } - } - - return true; -} - /********************************************************************************************************************* * Client storage options ********************************************************************************************************************/ @@ -3387,17 +3367,10 @@ if (aws_mqtt5_packet_connect_view_validate(options->connect_options)) { AWS_LOGF_ERROR(AWS_LS_MQTT5_GENERAL, "invalid CONNECT options in mqtt5 client configuration"); - /* connect validation failure will have already rasied the appropriate error */ + /* connect validation failure will have already raised the appropriate error */ return AWS_OP_ERR; } - /* The client will not behave properly if ping timeout is not significantly shorter than the keep alive interval */ - if (!aws_mqtt5_client_keep_alive_options_are_valid( - options->connect_options->keep_alive_interval_seconds, options->ping_timeout_ms)) { - AWS_LOGF_ERROR(AWS_LS_MQTT5_GENERAL, "keep alive interval is too small relative to ping timeout interval"); - return aws_raise_error(AWS_ERROR_MQTT5_CLIENT_OPTIONS_VALIDATION); - } - if (options->topic_aliasing_options != NULL) { if (!aws_mqtt5_outbound_topic_alias_behavior_type_validate( options->topic_aliasing_options->outbound_topic_alias_behavior)) { diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/aws-c-mqtt-0.10.3/source/v5/mqtt5_to_mqtt3_adapter.c new/aws-c-mqtt-0.10.4/source/v5/mqtt5_to_mqtt3_adapter.c --- old/aws-c-mqtt-0.10.3/source/v5/mqtt5_to_mqtt3_adapter.c 2024-02-29 17:06:37.000000000 +0100 +++ new/aws-c-mqtt-0.10.4/source/v5/mqtt5_to_mqtt3_adapter.c 2024-03-26 23:12:31.000000000 +0100 @@ -309,16 +309,6 @@ } } - /* The client will not behave properly if ping timeout is not significantly shorter than the keep alive interval */ - if (!aws_mqtt5_client_keep_alive_options_are_valid( - connection_options->keep_alive_time_secs, connection_options->ping_timeout_ms)) { - AWS_LOGF_ERROR( - AWS_LS_MQTT5_TO_MQTT3_ADAPTER, - "id=%p: mqtt3-to-5-adapter - keep alive interval is too small relative to ping timeout interval", - (void *)adapter); - return aws_raise_error(AWS_ERROR_MQTT5_CLIENT_OPTIONS_VALIDATION); - } - return AWS_OP_SUCCESS; } diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/aws-c-mqtt-0.10.3/tests/CMakeLists.txt new/aws-c-mqtt-0.10.4/tests/CMakeLists.txt --- old/aws-c-mqtt-0.10.3/tests/CMakeLists.txt 2024-02-29 17:06:37.000000000 +0100 +++ new/aws-c-mqtt-0.10.4/tests/CMakeLists.txt 2024-03-26 23:12:31.000000000 +0100 @@ -244,7 +244,6 @@ add_test_case(mqtt5_client_options_validation_failure_no_publish_received) add_test_case(mqtt5_client_options_validation_failure_invalid_socket_options) add_test_case(mqtt5_client_options_validation_failure_invalid_connect) -add_test_case(mqtt5_client_options_validation_failure_invalid_keep_alive) add_test_case(mqtt5_client_options_validation_failure_invalid_port) add_test_case(mqtt5_operation_subscribe_connection_settings_validation_failure_exceeds_maximum_packet_size) add_test_case(mqtt5_operation_unsubscribe_connection_settings_validation_failure_exceeds_maximum_packet_size) @@ -330,6 +329,8 @@ add_test_case(mqtt5_client_sub_pub_unsub_qos1) add_test_case(mqtt5_client_ping_sequence) add_test_case(mqtt5_client_ping_timeout) +add_test_case(mqtt5_client_ping_timeout_with_keep_alive_conflict) +add_test_case(mqtt5_client_disabled_keep_alive) add_test_case(mqtt5_client_reconnect_failure_backoff) add_test_case(mqtt5_client_reconnect_backoff_insufficient_reset) add_test_case(mqtt5_client_reconnect_backoff_sufficient_reset) diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/aws-c-mqtt-0.10.3/tests/v5/mqtt5_client_tests.c new/aws-c-mqtt-0.10.4/tests/v5/mqtt5_client_tests.c --- old/aws-c-mqtt-0.10.3/tests/v5/mqtt5_client_tests.c 2024-02-29 17:06:37.000000000 +0100 +++ new/aws-c-mqtt-0.10.4/tests/v5/mqtt5_client_tests.c 2024-03-26 23:12:31.000000000 +0100 @@ -986,10 +986,7 @@ struct aws_mqtt5_client_mock_test_fixture *test_fixture; }; -static bool s_received_at_least_n_pingreqs(void *arg) { - struct aws_mqtt5_client_test_wait_for_n_context *ping_context = arg; - struct aws_mqtt5_client_mock_test_fixture *test_fixture = ping_context->test_fixture; - +static size_t s_count_pingreqs(struct aws_mqtt5_client_mock_test_fixture *test_fixture) { size_t ping_count = 0; size_t packet_count = aws_array_list_length(&test_fixture->server_received_packets); for (size_t i = 0; i < packet_count; ++i) { @@ -1001,6 +998,15 @@ } } + return ping_count; +} + +static bool s_received_at_least_n_pingreqs(void *arg) { + struct aws_mqtt5_client_test_wait_for_n_context *ping_context = arg; + struct aws_mqtt5_client_mock_test_fixture *test_fixture = ping_context->test_fixture; + + size_t ping_count = s_count_pingreqs(test_fixture); + return ping_count >= ping_context->required_event_count; } @@ -1217,7 +1223,9 @@ #define TIMEOUT_TEST_PING_INTERVAL_MS ((uint64_t)10000) -static int s_verify_ping_timeout_interval(struct aws_mqtt5_client_mock_test_fixture *test_context) { +static int s_verify_ping_timeout_interval( + struct aws_mqtt5_client_mock_test_fixture *test_context, + uint64_t expected_connected_time_ms) { aws_mutex_lock(&test_context->lock); uint64_t connected_time = 0; @@ -1242,8 +1250,6 @@ uint64_t connected_interval_ms = aws_timestamp_convert(disconnected_time - connected_time, AWS_TIMESTAMP_NANOS, AWS_TIMESTAMP_MILLIS, NULL); - uint64_t expected_connected_time_ms = - TIMEOUT_TEST_PING_INTERVAL_MS + (uint64_t)test_context->client->config->ping_timeout_ms; ASSERT_TRUE(s_is_within_percentage_of(expected_connected_time_ms, connected_interval_ms, .3)); @@ -1310,7 +1316,9 @@ ASSERT_SUCCESS( s_verify_simple_lifecycle_event_sequence(&test_context, expected_events, AWS_ARRAY_SIZE(expected_events))); - ASSERT_SUCCESS(s_verify_ping_timeout_interval(&test_context)); + uint64_t expected_connected_time_ms = + TIMEOUT_TEST_PING_INTERVAL_MS + (uint64_t)test_context.client->config->ping_timeout_ms; + ASSERT_SUCCESS(s_verify_ping_timeout_interval(&test_context, expected_connected_time_ms)); enum aws_mqtt5_client_state expected_states[] = { AWS_MCS_CONNECTING, @@ -1329,6 +1337,147 @@ AWS_TEST_CASE(mqtt5_client_ping_timeout, s_mqtt5_client_ping_timeout_fn) +/* + * A variant of the basic ping timeout test that uses a timeout that is larger than the keep alive. Previously, + * we forbid this because taken literally, it leads to broken behavior. We now clamp the ping timeout dynamically + * based on the connection's established keep alive. + */ +static int s_mqtt5_client_ping_timeout_with_keep_alive_conflict_fn(struct aws_allocator *allocator, void *ctx) { + (void)ctx; + + aws_mqtt_library_init(allocator); + + struct mqtt5_client_test_options test_options; + aws_mqtt5_client_test_init_default_options(&test_options); + + /* fast keep alive in order keep tests reasonably short */ + uint16_t keep_alive_seconds = + (uint16_t)aws_timestamp_convert(TIMEOUT_TEST_PING_INTERVAL_MS, AWS_TIMESTAMP_MILLIS, AWS_TIMESTAMP_SECS, NULL); + test_options.connect_options.keep_alive_interval_seconds = keep_alive_seconds; + + /* don't respond to PINGREQs */ + test_options.server_function_table.packet_handlers[AWS_MQTT5_PT_PINGREQ] = NULL; + + /* ping timeout slower than keep alive */ + test_options.client_options.ping_timeout_ms = 2 * TIMEOUT_TEST_PING_INTERVAL_MS; + + struct aws_mqtt5_client_mqtt5_mock_test_fixture_options test_fixture_options = { + .client_options = &test_options.client_options, + .server_function_table = &test_options.server_function_table, + }; + + struct aws_mqtt5_client_mock_test_fixture test_context; + ASSERT_SUCCESS(aws_mqtt5_client_mock_test_fixture_init(&test_context, allocator, &test_fixture_options)); + + struct aws_mqtt5_client *client = test_context.client; + ASSERT_SUCCESS(aws_mqtt5_client_start(client)); + + aws_wait_for_connected_lifecycle_event(&test_context); + s_wait_for_disconnection_lifecycle_event(&test_context); + + ASSERT_SUCCESS(aws_mqtt5_client_stop(client, NULL, NULL)); + + aws_wait_for_stopped_lifecycle_event(&test_context); + + struct aws_mqtt5_client_lifecycle_event expected_events[] = { + { + .event_type = AWS_MQTT5_CLET_ATTEMPTING_CONNECT, + }, + { + .event_type = AWS_MQTT5_CLET_CONNECTION_SUCCESS, + }, + { + .event_type = AWS_MQTT5_CLET_DISCONNECTION, + .error_code = AWS_ERROR_MQTT5_PING_RESPONSE_TIMEOUT, + }, + { + .event_type = AWS_MQTT5_CLET_STOPPED, + }, + }; + ASSERT_SUCCESS( + s_verify_simple_lifecycle_event_sequence(&test_context, expected_events, AWS_ARRAY_SIZE(expected_events))); + + uint64_t expected_connected_time_ms = 3 * TIMEOUT_TEST_PING_INTERVAL_MS / 2; + ASSERT_SUCCESS(s_verify_ping_timeout_interval(&test_context, expected_connected_time_ms)); + + enum aws_mqtt5_client_state expected_states[] = { + AWS_MCS_CONNECTING, + AWS_MCS_MQTT_CONNECT, + AWS_MCS_CONNECTED, + AWS_MCS_CLEAN_DISCONNECT, + AWS_MCS_CHANNEL_SHUTDOWN, + }; + ASSERT_SUCCESS(aws_verify_client_state_sequence(&test_context, expected_states, AWS_ARRAY_SIZE(expected_states))); + + aws_mqtt5_client_mock_test_fixture_clean_up(&test_context); + aws_mqtt_library_clean_up(); + + return AWS_OP_SUCCESS; +} + +AWS_TEST_CASE( + mqtt5_client_ping_timeout_with_keep_alive_conflict, + s_mqtt5_client_ping_timeout_with_keep_alive_conflict_fn) + +/* + * Set up a zero keep alive and verify no pings get sent over an interval of time. + */ +static int s_mqtt5_client_disabled_keep_alive_fn(struct aws_allocator *allocator, void *ctx) { + (void)ctx; + + aws_mqtt_library_init(allocator); + + struct mqtt5_client_test_options test_options; + aws_mqtt5_client_test_init_default_options(&test_options); + + /* no keep alive at all */ + test_options.connect_options.keep_alive_interval_seconds = 0; + + struct aws_mqtt5_client_mqtt5_mock_test_fixture_options test_fixture_options = { + .client_options = &test_options.client_options, + .server_function_table = &test_options.server_function_table, + }; + + struct aws_mqtt5_client_mock_test_fixture test_context; + ASSERT_SUCCESS(aws_mqtt5_client_mock_test_fixture_init(&test_context, allocator, &test_fixture_options)); + + struct aws_mqtt5_client *client = test_context.client; + ASSERT_SUCCESS(aws_mqtt5_client_start(client)); + + aws_wait_for_connected_lifecycle_event(&test_context); + + uint16_t negotiated_keep_alive = 65535; + aws_mutex_lock(&test_context.lock); + size_t event_count = aws_array_list_length(&test_context.lifecycle_events); + struct aws_mqtt5_lifecycle_event_record *record = NULL; + aws_array_list_get_at(&test_context.lifecycle_events, &record, event_count - 1); + ASSERT_TRUE(AWS_MQTT5_CLET_CONNECTION_SUCCESS == record->event.event_type); + negotiated_keep_alive = record->settings_storage.server_keep_alive; + aws_mutex_unlock(&test_context.lock); + + ASSERT_INT_EQUALS(0, negotiated_keep_alive); + + // zzz + aws_thread_current_sleep(aws_timestamp_convert(5, AWS_TIMESTAMP_SECS, AWS_TIMESTAMP_NANOS, NULL)); + + ASSERT_SUCCESS(aws_mqtt5_client_stop(client, NULL, NULL)); + + aws_wait_for_stopped_lifecycle_event(&test_context); + + // verify the mock server did not get any PINGREQs + aws_mutex_lock(&test_context.lock); + size_t pingreq_count = s_count_pingreqs(&test_context); + aws_mutex_unlock(&test_context.lock); + ASSERT_INT_EQUALS(0, pingreq_count); + + aws_mqtt5_client_mock_test_fixture_clean_up(&test_context); + aws_mqtt_library_clean_up(); + + return AWS_OP_SUCCESS; +} + +AWS_TEST_CASE(mqtt5_client_disabled_keep_alive, s_mqtt5_client_disabled_keep_alive_fn) + struct aws_lifecycle_event_wait_context { enum aws_mqtt5_client_lifecycle_event_type type; size_t count; diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/aws-c-mqtt-0.10.3/tests/v5/mqtt5_operation_validation_failure_tests.c new/aws-c-mqtt-0.10.4/tests/v5/mqtt5_operation_validation_failure_tests.c --- old/aws-c-mqtt-0.10.3/tests/v5/mqtt5_operation_validation_failure_tests.c 2024-02-29 17:06:37.000000000 +0100 +++ new/aws-c-mqtt-0.10.4/tests/v5/mqtt5_operation_validation_failure_tests.c 2024-03-26 23:12:31.000000000 +0100 @@ -1170,20 +1170,6 @@ AWS_CLIENT_CREATION_VALIDATION_FAILURE(invalid_connect, s_good_client_options, s_make_invalid_connect_client_options) -static struct aws_mqtt5_packet_connect_view s_short_keep_alive_connect_view = { - .keep_alive_interval_seconds = 20, -}; - -static void s_make_invalid_keep_alive_client_options(struct aws_mqtt5_client_options *options) { - options->connect_options = &s_short_keep_alive_connect_view; - options->ping_timeout_ms = 30000; -} - -AWS_CLIENT_CREATION_VALIDATION_FAILURE( - invalid_keep_alive, - s_good_client_options, - s_make_invalid_keep_alive_client_options) - static void s_make_invalid_port_client_options(struct aws_mqtt5_client_options *options) { options->port = 0xFFFFFFFF; }