diff --git a/source/v5/mqtt5_client.c b/source/v5/mqtt5_client.c index f74b45fa..e6da6d3d 100644 --- a/source/v5/mqtt5_client.c +++ b/source/v5/mqtt5_client.c @@ -1069,7 +1069,11 @@ static void s_reset_ping(struct aws_mqtt5_client *client) { 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); diff --git a/tests/CMakeLists.txt b/tests/CMakeLists.txt index 630ad765..46bbc2b3 100644 --- a/tests/CMakeLists.txt +++ b/tests/CMakeLists.txt @@ -330,6 +330,7 @@ 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 --git a/tests/v5/mqtt5_client_tests.c b/tests/v5/mqtt5_client_tests.c index 066248be..267f5725 100644 --- a/tests/v5/mqtt5_client_tests.c +++ b/tests/v5/mqtt5_client_tests.c @@ -986,10 +986,7 @@ struct aws_mqtt5_client_test_wait_for_n_context { 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 @@ static bool s_received_at_least_n_pingreqs(void *arg) { } } + 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; } @@ -1413,6 +1419,65 @@ 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;