From b7de17b8f3cceea759d4e7525aeb71166be08912 Mon Sep 17 00:00:00 2001 From: Bret Ambrose Date: Wed, 29 Nov 2023 11:55:33 -0800 Subject: [PATCH 01/19] Checkpoint before context switch --- include/aws/mqtt/private/client_impl_shared.h | 19 + .../request-response/protocol_adapter.h | 96 +++++ source/client.c | 7 + source/client_impl_shared.c | 4 + source/request-response/protocol_adapter.c | 350 ++++++++++++++++++ source/v5/mqtt5_to_mqtt3_adapter.c | 7 + 6 files changed, 483 insertions(+) create mode 100644 include/aws/mqtt/private/request-response/protocol_adapter.h create mode 100644 source/request-response/protocol_adapter.c diff --git a/include/aws/mqtt/private/client_impl_shared.h b/include/aws/mqtt/private/client_impl_shared.h index d244cfe7..9f28894f 100644 --- a/include/aws/mqtt/private/client_impl_shared.h +++ b/include/aws/mqtt/private/client_impl_shared.h @@ -10,6 +10,20 @@ struct aws_mqtt_client_connection; +/* + * Internal enum that indicates what type of struct the underlying impl pointer actually is. We use this + * to safely interact with private APIs on the implementation or extract the adapted 5 client directly, as + * necessary. + */ +enum aws_mqtt311_impl_type { + + /* 311 connection impl can be cast to `struct aws_mqtt_client_connection_311_impl` */ + AWS_MQTT311_IT_311_CONNECTION_IMPL, + + /* 311 connection impl can be cast to `struct aws_mqtt_client_connection_5_impl`*/ + AWS_MQTT311_IT_5_ADAPTER_IMPL, +}; + struct aws_mqtt_client_connection_vtable { struct aws_mqtt_client_connection *(*acquire_fn)(void *impl); @@ -107,6 +121,8 @@ struct aws_mqtt_client_connection_vtable { void *userdata); int (*get_stats_fn)(void *impl, struct aws_mqtt_connection_operation_statistics *stats); + + enum aws_mqtt311_impl_type (*get_impl_type)(void *impl); }; struct aws_mqtt_client_connection { @@ -114,6 +130,9 @@ struct aws_mqtt_client_connection { void *impl; }; +AWS_MQTT_API enum aws_mqtt311_impl_type aws_mqtt_client_connection_get_impl_type( + struct aws_mqtt_client_connection *connection); + AWS_MQTT_API uint64_t aws_mqtt_hash_uint16_t(const void *item); AWS_MQTT_API bool aws_mqtt_compare_uint16_t_eq(const void *a, const void *b); diff --git a/include/aws/mqtt/private/request-response/protocol_adapter.h b/include/aws/mqtt/private/request-response/protocol_adapter.h new file mode 100644 index 00000000..7d7d9117 --- /dev/null +++ b/include/aws/mqtt/private/request-response/protocol_adapter.h @@ -0,0 +1,96 @@ +#ifndef AWS_MQTT_PRIVATE_REQUEST_RESPONSE_PROTOCOL_ADAPTER_H +#define AWS_MQTT_PRIVATE_REQUEST_RESPONSE_PROTOCOL_ADAPTER_H + +/** + * Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. + * SPDX-License-Identifier: Apache-2.0. + */ + +#include + +#include + +struct aws_allocator; +struct aws_mqtt_client_connection; +struct aws_mqtt5_client; + +struct aws_protocol_adapter_subscribe_options { + struct aws_byte_cursor topic_filter; + uint32_t ack_timeout_seconds; +}; + +struct aws_protocol_adapter_unsubscribe_options { + struct aws_byte_cursor topic_filter; + uint32_t ack_timeout_seconds; +}; + +struct aws_protocol_adapter_publish_options { + struct aws_byte_cursor topic; + struct aws_byte_cursor payload; + + void (*completion_callback_fn)(int, void *); + void *user_data; + uint32_t ack_timeout_seconds; +}; + +enum aws_protocol_adapter_subscription_status_update { + AWS_PASS_ESTABLISHMENT_SUCCESS, + AWS_PASS_ESTABLISHMENT_FAILURE, + AWS_PASS_REMOVED +}; + +struct aws_protocol_adapter_subscription_status_update_event { + struct aws_byte_cursor topic_filter; + enum aws_protocol_adapter_subscription_status_update status_update; +}; + +struct aws_protocol_adapter_incoming_publish_event { + struct aws_byte_cursor topic; + struct aws_byte_cursor payload; +}; + +typedef void(aws_protocol_adapter_subscription_status_fn)(struct aws_protocol_adapter_subscription_status_update_event *update, void *user_data); +typedef void(aws_protocol_adapter_incoming_publish_fn)(struct aws_protocol_adapter_incoming_publish_event *publish, void *user_data); +typedef void(aws_protocol_adapter_terminate_callback_fn)(void *user_data); + +struct aws_mqtt_protocol_adapter_options { + aws_protocol_adapter_subscription_status_fn *subscription_status_update_callback; + aws_protocol_adapter_incoming_publish_fn *incoming_publish_callback; + aws_protocol_adapter_terminate_callback_fn *terminate_callback; + + void *user_data; +}; + +struct aws_mqtt_protocol_adapter_vtable { + + void (*aws_mqtt_protocol_adapter_release_fn)(void *); + + int (*aws_mqtt_protocol_adapter_subscribe_fn)(void *, struct aws_protocol_adapter_subscribe_options *); + + int (*aws_mqtt_protocol_adapter_unsubscribe_fn)(void *, struct aws_protocol_adapter_unsubscribe_options *); + + int (*aws_mqtt_protocol_adapter_publish_fn)(void *, struct aws_protocol_adapter_publish_options *); +}; + +struct aws_mqtt_protocol_adapter { + const struct aws_mqtt_protocol_adapter_vtable *vtable; + void *impl; +}; + +AWS_EXTERN_C_BEGIN + +AWS_MQTT_API struct aws_mqtt_protocol_adapter *aws_mqtt_protocol_adapter_new_from_311(struct aws_allocator *allocator, struct aws_mqtt_protocol_adapter_options *options, struct aws_mqtt_client_connection *connection); + +AWS_MQTT_API struct aws_mqtt_protocol_adapter *aws_mqtt_protocol_adapter_new_from_5(struct aws_allocator *allocator, struct aws_mqtt_protocol_adapter_options *options, struct aws_mqtt5_client *client); + +AWS_MQTT_API void aws_mqtt_protocol_adapter_release(struct aws_mqtt_protocol_adapter *adapter); + +AWS_MQTT_API int aws_mqtt_protocol_adapter_subscribe(struct aws_mqtt_protocol_adapter *adapter, struct aws_protocol_adapter_subscribe_options *options); + +AWS_MQTT_API int aws_mqtt_protocol_adapter_unsubscribe(struct aws_mqtt_protocol_adapter *adapter, struct aws_protocol_adapter_unsubscribe_options *options); + +AWS_MQTT_API int aws_mqtt_protocol_adapter_publish(struct aws_mqtt_protocol_adapter *adapter, struct aws_protocol_adapter_publish_options *options); + +AWS_EXTERN_C_END + +#endif /* AWS_MQTT_PRIVATE_REQUEST_RESPONSE_PROTOCOL_ADAPTER_H */ diff --git a/source/client.c b/source/client.c index c332c4da..42ab634c 100644 --- a/source/client.c +++ b/source/client.c @@ -3220,6 +3220,12 @@ static void s_aws_mqtt_client_connection_311_release(void *impl) { aws_ref_count_release(&connection->ref_count); } +enum aws_mqtt311_impl_type s_aws_mqtt_client_connection_3_get_impl(void *impl) { + (void)impl; + + return AWS_MQTT311_IT_311_CONNECTION_IMPL; +} + static struct aws_mqtt_client_connection_vtable s_aws_mqtt_client_connection_311_vtable = { .acquire_fn = s_aws_mqtt_client_connection_311_acquire, .release_fn = s_aws_mqtt_client_connection_311_release, @@ -3243,6 +3249,7 @@ static struct aws_mqtt_client_connection_vtable s_aws_mqtt_client_connection_311 .unsubscribe_fn = s_aws_mqtt_client_connection_311_unsubscribe, .publish_fn = s_aws_mqtt_client_connection_311_publish, .get_stats_fn = s_aws_mqtt_client_connection_311_get_stats, + .get_impl_type = s_aws_mqtt_client_connection_3_get_impl, }; static struct aws_mqtt_client_connection_vtable *s_aws_mqtt_client_connection_311_vtable_ptr = diff --git a/source/client_impl_shared.c b/source/client_impl_shared.c index 6f65eb88..0f0e70a2 100644 --- a/source/client_impl_shared.c +++ b/source/client_impl_shared.c @@ -204,6 +204,10 @@ int aws_mqtt_client_connection_get_stats( return (*connection->vtable->get_stats_fn)(connection->impl, stats); } +enum aws_mqtt311_impl_type aws_mqtt_client_connection_get_impl_type(struct aws_mqtt_client_connection *connection) { + return (*connection->vtable->get_impl_type)(connection->impl); +} + uint64_t aws_mqtt_hash_uint16_t(const void *item) { return *(uint16_t *)item; } diff --git a/source/request-response/protocol_adapter.c b/source/request-response/protocol_adapter.c new file mode 100644 index 00000000..d29f705d --- /dev/null +++ b/source/request-response/protocol_adapter.c @@ -0,0 +1,350 @@ +/** + * Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. + * SPDX-License-Identifier: Apache-2.0. + */ + +#include + +#include +#include +#include +#include +#include + +struct aws_protocol_adapter_weak_ref { + struct aws_allocator *allocator; + struct aws_ref_count refcount; + void *referenced; +}; + +static void s_destroy_protocol_adapter_weak_ref(void *value) { + struct aws_protocol_adapter_weak_ref *weak_ref = value; + + aws_mem_release(weak_ref->allocator, weak_ref); +} + +struct aws_protocol_adapter_weak_ref *aws_protocol_adapter_weak_ref_new(struct aws_allocator *allocator, void *referenced) { + struct aws_protocol_adapter_weak_ref *weak_ref = aws_mem_calloc(allocator, 1, sizeof(struct aws_protocol_adapter_weak_ref)); + + aws_ref_count_init(&weak_ref->refcount, weak_ref, s_destroy_protocol_adapter_weak_ref); + weak_ref->allocator = allocator; + weak_ref->referenced = referenced; + + return weak_ref; +} + +struct aws_protocol_adapter_weak_ref *aws_protocol_adapter_weak_ref_acquire(struct aws_protocol_adapter_weak_ref *weak_ref) { + if (NULL != weak_ref) { + aws_ref_count_acquire(&weak_ref->refcount); + } + + return weak_ref; +} + +struct aws_protocol_adapter_weak_ref *aws_protocol_adapter_weak_ref_release(struct aws_protocol_adapter_weak_ref *weak_ref) { + if (NULL != weak_ref) { + aws_ref_count_release(&weak_ref->refcount); + } + + return NULL; +} + +/******************************************************************************************************************/ + +enum aws_protocol_adapter_subscription_status { + PASS_NONE, + PASS_SUBSCRIBING, + PASS_SUBSCRIBED, + PASS_UNSUBSCRIBING, +}; + +struct aws_mqtt_protocol_adapter_subscription { + struct aws_allocator *allocator; + + struct aws_byte_cursor topic_filter; + struct aws_byte_buf topic_filter_buf; + + enum aws_protocol_adapter_subscription_status status; +}; + +static struct aws_mqtt_protocol_adapter_subscription *s_aws_mqtt_protocol_adapter_subscription_new(struct aws_allocator *allocator, struct aws_byte_cursor topic_filter) { + struct aws_mqtt_protocol_adapter_subscription *subscription = aws_mem_calloc(allocator, 1, sizeof(struct aws_mqtt_protocol_adapter_subscription)); + + subscription->allocator = allocator; + aws_byte_buf_init_copy_from_cursor(&subscription->topic_filter_buf, allocator, topic_filter); + subscription->topic_filter = aws_byte_cursor_from_buf(&subscription->topic_filter_buf); + + return subscription; +} + +static void s_aws_mqtt_protocol_adapter_subscription_destroy(struct aws_mqtt_protocol_adapter_subscription *subscription) { + aws_byte_buf_clean_up(&subscription->topic_filter_buf); + aws_mem_release(subscription->allocator, subscription); +} + +struct aws_mqtt_protocol_adapter_subscription_set { + struct aws_allocator *allocator; + struct aws_hash_table subscriptions; // aws_byte_cursor * -> aws_mqtt_protocol_adapter_subscription * + + aws_protocol_adapter_subscription_status_fn *subscription_status_update_callback; + void *callback_user_data; +}; + +static int s_aws_mqtt_protocol_adapter_subscription_set_init(struct aws_mqtt_protocol_adapter_subscription_set *subscription_set, struct aws_allocator *allocator, struct aws_mqtt_protocol_adapter_options *options) { + subscription_set->allocator = allocator; + subscription_set->subscription_status_update_callback = options->subscription_status_update_callback; + subscription_set->callback_user_data = options->user_data; + + return aws_hash_table_init(&subscription_set->subscriptions, allocator, 0, aws_hash_byte_cursor_ptr, aws_mqtt_byte_cursor_hash_equality, NULL, NULL); +} + +static int s_aws_mqtt_protocol_adapter_subscription_set_subscription_clean_up(void *context, struct aws_hash_element *elem) { + struct aws_mqtt_protocol_adapter *adapter = context; + + struct aws_mqtt_protocol_adapter_subscription *subscription = elem->value; + + if (subscription->status != PASS_UNSUBSCRIBING) { + struct aws_protocol_adapter_unsubscribe_options options = { + .topic_filter = subscription->topic_filter, + }; + + aws_mqtt_protocol_adapter_unsubscribe(adapter, &options); + } + + s_aws_mqtt_protocol_adapter_subscription_destroy(subscription); + + return AWS_COMMON_HASH_TABLE_ITER_CONTINUE | AWS_COMMON_HASH_TABLE_ITER_DELETE; +} + +static void s_aws_mqtt_protocol_adapter_subscription_set_clean_up(struct aws_mqtt_protocol_adapter_subscription_set *subscription_set, struct aws_mqtt_protocol_adapter *owner) { + struct aws_hash_table subscriptions; + AWS_ZERO_STRUCT(subscriptions); + + aws_hash_table_swap(&subscription_set->subscriptions, &subscriptions); + + aws_hash_table_foreach(&subscriptions, s_aws_mqtt_protocol_adapter_subscription_set_subscription_clean_up, owner); + + aws_hash_table_clean_up(&subscriptions); +} + +static void s_aws_mqtt_protocol_adapter_subscription_set_on_subscribe_completion(struct aws_mqtt_protocol_adapter_subscription_set *subscription_set, struct aws_byte_cursor topic_filter, bool success) { + (void)subscription_set; + (void)topic_filter; + (void)success; + + ??; +} + +static void s_aws_mqtt_protocol_adapter_subscription_set_update_subscription(struct aws_mqtt_protocol_adapter_subscription_set *subscription_set, struct aws_byte_cursor topic_filter, enum aws_protocol_adapter_subscription_status status) { + (void)subscription_set; + (void)topic_filter; + (void)status; + + ??; +} + +static void s_aws_mqtt_protocol_adapter_subscription_set_create_or_update_subscription(struct aws_mqtt_protocol_adapter_subscription_set *subscription_set, struct aws_byte_cursor topic_filter, enum aws_protocol_adapter_subscription_status status) { + (void)subscription_set; + (void)topic_filter; + (void)status; + + ??; +} + + +/******************************************************************************************************************/ + +struct aws_mqtt_protocol_adapter *aws_mqtt_protocol_adapter_new_from_311(struct aws_allocator *allocator, struct aws_mqtt_protocol_adapter_options *options, struct aws_mqtt_client_connection *connection) { + (void)allocator; + (void)options; + (void)connection; + + return NULL; +} + +/******************************************************************************************************************/ + +struct aws_mqtt_protocol_adapter_5_impl { + struct aws_allocator *allocator; + struct aws_mqtt_protocol_adapter base; + struct aws_protocol_adapter_weak_ref *callback_ref; + struct aws_mqtt_protocol_adapter_options config; + + struct aws_event_loop *loop; + struct aws_mqtt5_client *client; + struct aws_mqtt5_listener *listener; + + struct aws_mqtt_protocol_adapter_subscription_set subscriptions; +}; + +static void s_aws_mqtt_protocol_adapter_5_release(void *impl) { + struct aws_mqtt_protocol_adapter_5_impl *adapter = impl; + + aws_mqtt5_listener_release(adapter->listener); +} + +struct aws_mqtt_protocol_adapter_5_subscribe_data { + struct aws_allocator *allocator; + + struct aws_byte_buf *topic_filter; + struct aws_protocol_adapter_weak_ref *callback_ref; +}; + +static struct aws_mqtt_protocol_adapter_5_subscribe_data *aws_mqtt_protocol_adapter_5_subscribe_data_new(struct aws_allocator *allocator, struct aws_byte_cursor topic_filter, struct aws_protocol_adapter_weak_ref *callback_ref) { + struct aws_mqtt_protocol_adapter_5_subscribe_data *subscribe_data = aws_mem_calloc(allocator, 1, sizeof(struct aws_mqtt_protocol_adapter_5_subscribe_data)); + + subscribe_data->allocator = allocator; + subscribe_data->callback_ref = aws_protocol_adapter_weak_ref_acquire(callback_ref); + aws_byte_buf_init_copy_from_cursor(&subscribe_data->topic_filter, allocator, topic_filter); + + return subscribe_data; +} + +static void aws_mqtt_protocol_adapter_5_subscribe_data_delete(struct aws_mqtt_protocol_adapter_5_subscribe_data *subscribe_data) { + aws_protocol_adapter_weak_ref_release(subscribe_data->callback_ref); + + aws_mem_release(subscribe_data->allocator, subscribe_data); +} + +static void s_protocol_adapter_5_subscribe_completion(const struct aws_mqtt5_packet_suback_view *suback, + int error_code, + void *complete_ctx) { + struct aws_mqtt_protocol_adapter_5_subscribe_data *subscribe_data = complete_ctx; + struct aws_mqtt_protocol_adapter_5_impl *adapter = subscribe_data->callback_ref->referenced; + + if (adapter == NULL) { + goto done; + } + + bool success = error_code == AWS_ERROR_SUCCESS && suback != NULL && suback->reason_code_count == 1 && suback->reason_codes[0] <= AWS_MQTT5_SARC_GRANTED_QOS_1; + s_aws_mqtt_protocol_adapter_subscription_set_on_subscribe_completion(&adapter->config, &adapter->subscriptions, aws_byte_cursor_from_buf(&subscribe_data->topic_filter), success); + +done: + + aws_mqtt_protocol_adapter_5_subscribe_data_delete(subscribe_data); +} + +int s_aws_mqtt_protocol_adapter_5_subscribe(void *impl, struct aws_protocol_adapter_subscribe_options *options) { + struct aws_mqtt_protocol_adapter_5_impl *adapter = impl; + + struct aws_mqtt_protocol_adapter_5_subscribe_data *subscribe_data = aws_mqtt_protocol_adapter_5_subscribe_data_new(adapter->allocator, options->topic_filter, adapter->callback_ref); + + struct aws_mqtt5_subscription_view subscription_view = { + .qos = AWS_MQTT5_QOS_AT_LEAST_ONCE, + .topic_filter = options->topic_filter, + }; + + struct aws_mqtt5_packet_subscribe_view subscribe_view = { + .subscriptions = &subscription_view, + .subscription_count = 1, + }; + + struct aws_mqtt5_subscribe_completion_options completion_options = { + .ack_timeout_seconds_override = options->ack_timeout_seconds, + .completion_callback = s_protocol_adapter_5_subscribe_completion, + .completion_user_data = subscribe_data, + }; + + if (aws_mqtt5_client_subscribe(adapter->client, &subscribe_view, &completion_options)) { + goto error; + } + + return AWS_OP_SUCCESS; + +error: + + aws_mqtt_protocol_adapter_5_subscribe_data_delete(subscribe_data); + + return AWS_OP_ERR; +} + +int s_aws_mqtt_protocol_adapter_5_unsubscribe(void *impl, struct aws_protocol_adapter_unsubscribe_options *options) { + struct aws_mqtt_protocol_adapter_5_impl *adapter = impl; + (void)adapter; + (void)options; + + return aws_raise_error(AWS_ERROR_UNIMPLEMENTED); +} + +int s_aws_mqtt_protocol_adapter_5_publish(void *impl, struct aws_protocol_adapter_publish_options *options) { + struct aws_mqtt_protocol_adapter_5_impl *adapter = impl; + (void)adapter; + (void)options; + + return aws_raise_error(AWS_ERROR_UNIMPLEMENTED); +} + +static bool s_protocol_adapter_mqtt5_listener_publish_received(const struct aws_mqtt5_packet_publish_view *publish, void *user_data) { + (void)publish; + (void)user_data; +} + +static void s_protocol_adapter_mqtt5_lifecycle_event_callback(const struct aws_mqtt5_client_lifecycle_event *event) { + (void)event; +} + +static void s_protocol_adapter_mqtt5_listener_termination_callback(void *user_data) { + struct aws_mqtt_protocol_adapter_5_impl *adapter = user_data; + + AWS_FATAL_ASSERT(aws_event_loop_thread_is_callers_thread(adapter->client->loop)); + + aws_mqtt5_client_release(adapter->client); + + adapter->callback_ref->referenced = NULL; + aws_protocol_adapter_weak_ref_release(adapter->callback_ref); + + aws_mem_release(adapter->allocator, adapter); +} + +static struct aws_mqtt_protocol_adapter_vtable s_protocol_adapter_mqtt5_vtable = { + .aws_mqtt_protocol_adapter_release_fn = s_aws_mqtt_protocol_adapter_5_release, + .aws_mqtt_protocol_adapter_subscribe_fn = s_aws_mqtt_protocol_adapter_5_subscribe, + .aws_mqtt_protocol_adapter_unsubscribe_fn = s_aws_mqtt_protocol_adapter_5_unsubscribe, + .aws_mqtt_protocol_adapter_publish_fn = s_aws_mqtt_protocol_adapter_5_publish, +}; + +struct aws_mqtt_protocol_adapter *aws_mqtt_protocol_adapter_new_from_5(struct aws_allocator *allocator, struct aws_mqtt_protocol_adapter_options *options, struct aws_mqtt5_client *client) { + AWS_FATAL_ASSERT(aws_event_loop_thread_is_callers_thread(client->loop)); + + struct aws_mqtt_protocol_adapter_5_impl *adapter = aws_mem_calloc(allocator, 1, sizeof(struct aws_mqtt_protocol_adapter_5_impl)); + + adapter->allocator = allocator; + adapter->base.impl = adapter; + adapter->base.vtable = &s_protocol_adapter_mqtt5_vtable; + adapter->callback_ref = aws_protocol_adapter_weak_ref_new(allocator, adapter); + adapter->config = *options; + adapter->loop = client->loop; + adapter->client = aws_mqtt5_client_acquire(client); + + struct aws_mqtt5_listener_config listener_options = { + .client = client, + .listener_callbacks = { + .listener_publish_received_handler = s_protocol_adapter_mqtt5_listener_publish_received, + .listener_publish_received_handler_user_data = adapter, + .lifecycle_event_handler = s_protocol_adapter_mqtt5_lifecycle_event_callback, + .lifecycle_event_handler_user_data = adapter + }, + .termination_callback = s_protocol_adapter_mqtt5_listener_termination_callback, + .termination_callback_user_data = adapter, + }; + + adapter->listener = aws_mqtt5_listener_new(allocator, &listener_options); + + return adapter; +} + +void aws_mqtt_protocol_adapter_release(struct aws_mqtt_protocol_adapter *adapter) { + (*adapter->vtable->aws_mqtt_protocol_adapter_release_fn)(adapter->impl); +} + +int aws_mqtt_protocol_adapter_subscribe(struct aws_mqtt_protocol_adapter *adapter, struct aws_protocol_adapter_subscribe_options *options) { + return (*adapter->vtable->aws_mqtt_protocol_adapter_subscribe_fn)(adapter->impl, options); +} + +int aws_mqtt_protocol_adapter_unsubscribe(struct aws_mqtt_protocol_adapter *adapter, struct aws_protocol_adapter_unsubscribe_options *options) { + return (*adapter->vtable->aws_mqtt_protocol_adapter_unsubscribe_fn)(adapter->impl, options); +} + +int aws_mqtt_protocol_adapter_publish(struct aws_mqtt_protocol_adapter *adapter, struct aws_protocol_adapter_publish_options *options) { + return (*adapter->vtable->aws_mqtt_protocol_adapter_publish_fn)(adapter->impl, options); +} \ No newline at end of file diff --git a/source/v5/mqtt5_to_mqtt3_adapter.c b/source/v5/mqtt5_to_mqtt3_adapter.c index e48ca3d3..c89da9c0 100644 --- a/source/v5/mqtt5_to_mqtt3_adapter.c +++ b/source/v5/mqtt5_to_mqtt3_adapter.c @@ -2854,6 +2854,12 @@ static uint16_t s_aws_mqtt_5_resubscribe_existing_topics( return 0; } +enum aws_mqtt311_impl_type s_aws_mqtt_client_connection_5_get_impl(void *impl) { + (void)impl; + + return AWS_MQTT311_IT_5_ADAPTER_IMPL; +} + static struct aws_mqtt_client_connection_vtable s_aws_mqtt_client_connection_5_vtable = { .acquire_fn = s_aws_mqtt_client_connection_5_acquire, .release_fn = s_aws_mqtt_client_connection_5_release, @@ -2877,6 +2883,7 @@ static struct aws_mqtt_client_connection_vtable s_aws_mqtt_client_connection_5_v .unsubscribe_fn = s_aws_mqtt_client_connection_5_unsubscribe, .publish_fn = s_aws_mqtt_client_connection_5_publish, .get_stats_fn = s_aws_mqtt_client_connection_5_get_stats, + .get_impl_type = s_aws_mqtt_client_connection_5_get_impl, }; static struct aws_mqtt_client_connection_vtable *s_aws_mqtt_client_connection_5_vtable_ptr = From f501fa03e3701a27c3095f5fb15e5b8dce25ce13 Mon Sep 17 00:00:00 2001 From: Bret Ambrose Date: Mon, 11 Dec 2023 11:27:59 -0800 Subject: [PATCH 02/19] Checkpoint --- .../mqtt/private/request-response/weak_ref.h | 30 ++++++ source/request-response/protocol_adapter.c | 100 ++++++++---------- source/request-response/weak_ref.c | 54 ++++++++++ 3 files changed, 127 insertions(+), 57 deletions(-) create mode 100644 include/aws/mqtt/private/request-response/weak_ref.h create mode 100644 source/request-response/weak_ref.c diff --git a/include/aws/mqtt/private/request-response/weak_ref.h b/include/aws/mqtt/private/request-response/weak_ref.h new file mode 100644 index 00000000..34968874 --- /dev/null +++ b/include/aws/mqtt/private/request-response/weak_ref.h @@ -0,0 +1,30 @@ +#ifndef AWS_MQTT_PRIVATE_REQUEST_RESPONSE_WEAK_REF_H +#define AWS_MQTT_PRIVATE_REQUEST_RESPONSE_WEAK_REF_H + +/** + * Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. + * SPDX-License-Identifier: Apache-2.0. + */ + +#include + +#include + +struct aws_weak_ref; + +AWS_EXTERN_C_BEGIN + +AWS_MQTT_API struct aws_weak_ref *aws_weak_ref_new(struct aws_allocator *allocator, void *referenced); + +AWS_MQTT_API struct aws_weak_ref *aws_weak_ref_acquire(struct aws_weak_ref *weak_ref); + +AWS_MQTT_API struct aws_weak_ref *aws_weak_ref_release(struct aws_weak_ref *weak_ref); + +AWS_MQTT_API void *aws_weak_ref_get_reference(struct aws_weak_ref *weak_ref); + +AWS_MQTT_API void aws_weak_ref_zero_reference(struct aws_weak_ref *weak_ref); + +AWS_EXTERN_C_END + + +#endif /* AWS_MQTT_PRIVATE_REQUEST_RESPONSE_WEAK_REF_H */ diff --git a/source/request-response/protocol_adapter.c b/source/request-response/protocol_adapter.c index d29f705d..7c91bda2 100644 --- a/source/request-response/protocol_adapter.c +++ b/source/request-response/protocol_adapter.c @@ -7,50 +7,11 @@ #include #include +#include #include #include #include -struct aws_protocol_adapter_weak_ref { - struct aws_allocator *allocator; - struct aws_ref_count refcount; - void *referenced; -}; - -static void s_destroy_protocol_adapter_weak_ref(void *value) { - struct aws_protocol_adapter_weak_ref *weak_ref = value; - - aws_mem_release(weak_ref->allocator, weak_ref); -} - -struct aws_protocol_adapter_weak_ref *aws_protocol_adapter_weak_ref_new(struct aws_allocator *allocator, void *referenced) { - struct aws_protocol_adapter_weak_ref *weak_ref = aws_mem_calloc(allocator, 1, sizeof(struct aws_protocol_adapter_weak_ref)); - - aws_ref_count_init(&weak_ref->refcount, weak_ref, s_destroy_protocol_adapter_weak_ref); - weak_ref->allocator = allocator; - weak_ref->referenced = referenced; - - return weak_ref; -} - -struct aws_protocol_adapter_weak_ref *aws_protocol_adapter_weak_ref_acquire(struct aws_protocol_adapter_weak_ref *weak_ref) { - if (NULL != weak_ref) { - aws_ref_count_acquire(&weak_ref->refcount); - } - - return weak_ref; -} - -struct aws_protocol_adapter_weak_ref *aws_protocol_adapter_weak_ref_release(struct aws_protocol_adapter_weak_ref *weak_ref) { - if (NULL != weak_ref) { - aws_ref_count_release(&weak_ref->refcount); - } - - return NULL; -} - -/******************************************************************************************************************/ - enum aws_protocol_adapter_subscription_status { PASS_NONE, PASS_SUBSCRIBING, @@ -84,25 +45,27 @@ static void s_aws_mqtt_protocol_adapter_subscription_destroy(struct aws_mqtt_pro struct aws_mqtt_protocol_adapter_subscription_set { struct aws_allocator *allocator; + struct aws_mqtt_protocol_adapter *owner; // not an acquired reference due to the parent-child relationship struct aws_hash_table subscriptions; // aws_byte_cursor * -> aws_mqtt_protocol_adapter_subscription * aws_protocol_adapter_subscription_status_fn *subscription_status_update_callback; void *callback_user_data; }; -static int s_aws_mqtt_protocol_adapter_subscription_set_init(struct aws_mqtt_protocol_adapter_subscription_set *subscription_set, struct aws_allocator *allocator, struct aws_mqtt_protocol_adapter_options *options) { +static int s_aws_mqtt_protocol_adapter_subscription_set_init(struct aws_mqtt_protocol_adapter_subscription_set *subscription_set, struct aws_allocator *allocator, struct aws_mqtt_protocol_adapter *owner, struct aws_mqtt_protocol_adapter_options *options) { subscription_set->allocator = allocator; + subscription_set->owner = owner; subscription_set->subscription_status_update_callback = options->subscription_status_update_callback; subscription_set->callback_user_data = options->user_data; return aws_hash_table_init(&subscription_set->subscriptions, allocator, 0, aws_hash_byte_cursor_ptr, aws_mqtt_byte_cursor_hash_equality, NULL, NULL); } -static int s_aws_mqtt_protocol_adapter_subscription_set_subscription_clean_up(void *context, struct aws_hash_element *elem) { - struct aws_mqtt_protocol_adapter *adapter = context; +static int s_aws_mqtt_protocol_adapter_subscription_set_subscription_destroy(void *context, struct aws_hash_element *elem) { + struct aws_mqtt_protocol_adapter_subscription_set *subscription_set = context; + struct aws_mqtt_protocol_adapter *adapter = subscription_set->owner; struct aws_mqtt_protocol_adapter_subscription *subscription = elem->value; - if (subscription->status != PASS_UNSUBSCRIBING) { struct aws_protocol_adapter_unsubscribe_options options = { .topic_filter = subscription->topic_filter, @@ -116,23 +79,46 @@ static int s_aws_mqtt_protocol_adapter_subscription_set_subscription_clean_up(vo return AWS_COMMON_HASH_TABLE_ITER_CONTINUE | AWS_COMMON_HASH_TABLE_ITER_DELETE; } -static void s_aws_mqtt_protocol_adapter_subscription_set_clean_up(struct aws_mqtt_protocol_adapter_subscription_set *subscription_set, struct aws_mqtt_protocol_adapter *owner) { +static void s_aws_mqtt_protocol_adapter_subscription_set_clean_up(struct aws_mqtt_protocol_adapter_subscription_set *subscription_set) { struct aws_hash_table subscriptions; AWS_ZERO_STRUCT(subscriptions); aws_hash_table_swap(&subscription_set->subscriptions, &subscriptions); - aws_hash_table_foreach(&subscriptions, s_aws_mqtt_protocol_adapter_subscription_set_subscription_clean_up, owner); + aws_hash_table_foreach(&subscriptions, s_aws_mqtt_protocol_adapter_subscription_set_subscription_destroy, subscription_set); aws_hash_table_clean_up(&subscriptions); } +/* + * On subscribe success: if there's not an entry, is this possible? No because we're called only by a function that checks for adapter weak->strong first, so the adapter exists and we don't allow subscription removal without an unsubscribe complete and we don't allow the subscribe until the unsubscribe has completed. But what + * if + * On subscribe success: if there's an entry, transition | subscribing -> subscribed, send an update + * On subscribe failure: if there's not an entry, is this possible? + * On subscribe failure: if there's an entry, transition -> unsubscribing, send an update + * + * Should we just blindly add if the adapter exists? Yes: simplest. No: represents undefined behavior if it shouldn't be happening + * + * In the design we said that the subscription set is just a dumb reflection of the ordered sequence of operations + * from the rr client which implies we should just create_or_update. The only time we don't want to create_or_update + * is if we're in/post destruction but then there's no adapter and we early out + */ static void s_aws_mqtt_protocol_adapter_subscription_set_on_subscribe_completion(struct aws_mqtt_protocol_adapter_subscription_set *subscription_set, struct aws_byte_cursor topic_filter, bool success) { - (void)subscription_set; - (void)topic_filter; - (void)success; + if (!success) { + struct aws_protocol_adapter_unsubscribe_options options = { + .topic_filter = topic_filter, + }; + + aws_mqtt_protocol_adapter_unsubscribe(subscription_set->owner, &options); + } + + struct aws_hash_element *hash_element = NULL; + if (!aws_hash_table_find(&subscription_set->subscriptions, &topic_filter, &hash_element) || hash_element == NULL) { + return; + } + + struct aws_mqtt_protocol_adapter_subscription *subscription = hash_element->value; - ??; } static void s_aws_mqtt_protocol_adapter_subscription_set_update_subscription(struct aws_mqtt_protocol_adapter_subscription_set *subscription_set, struct aws_byte_cursor topic_filter, enum aws_protocol_adapter_subscription_status status) { @@ -194,14 +180,14 @@ static struct aws_mqtt_protocol_adapter_5_subscribe_data *aws_mqtt_protocol_adap struct aws_mqtt_protocol_adapter_5_subscribe_data *subscribe_data = aws_mem_calloc(allocator, 1, sizeof(struct aws_mqtt_protocol_adapter_5_subscribe_data)); subscribe_data->allocator = allocator; - subscribe_data->callback_ref = aws_protocol_adapter_weak_ref_acquire(callback_ref); + subscribe_data->callback_ref = aws_weak_ref_acquire(callback_ref); aws_byte_buf_init_copy_from_cursor(&subscribe_data->topic_filter, allocator, topic_filter); return subscribe_data; } static void aws_mqtt_protocol_adapter_5_subscribe_data_delete(struct aws_mqtt_protocol_adapter_5_subscribe_data *subscribe_data) { - aws_protocol_adapter_weak_ref_release(subscribe_data->callback_ref); + aws_weak_ref_release(subscribe_data->callback_ref); aws_mem_release(subscribe_data->allocator, subscribe_data); } @@ -210,14 +196,14 @@ static void s_protocol_adapter_5_subscribe_completion(const struct aws_mqtt5_pac int error_code, void *complete_ctx) { struct aws_mqtt_protocol_adapter_5_subscribe_data *subscribe_data = complete_ctx; - struct aws_mqtt_protocol_adapter_5_impl *adapter = subscribe_data->callback_ref->referenced; + struct aws_mqtt_protocol_adapter_5_impl *adapter = aws_weak_ref_get_reference(subscribe_data->callback_ref); if (adapter == NULL) { goto done; } bool success = error_code == AWS_ERROR_SUCCESS && suback != NULL && suback->reason_code_count == 1 && suback->reason_codes[0] <= AWS_MQTT5_SARC_GRANTED_QOS_1; - s_aws_mqtt_protocol_adapter_subscription_set_on_subscribe_completion(&adapter->config, &adapter->subscriptions, aws_byte_cursor_from_buf(&subscribe_data->topic_filter), success); + s_aws_mqtt_protocol_adapter_subscription_set_on_subscribe_completion(&adapter->subscriptions, aws_byte_cursor_from_buf(&subscribe_data->topic_filter), success); done: @@ -290,8 +276,8 @@ static void s_protocol_adapter_mqtt5_listener_termination_callback(void *user_da aws_mqtt5_client_release(adapter->client); - adapter->callback_ref->referenced = NULL; - aws_protocol_adapter_weak_ref_release(adapter->callback_ref); + aws_weak_ref_zero_reference(adapter->callback_ref); + aws_weak_ref_release(adapter->callback_ref); aws_mem_release(adapter->allocator, adapter); } @@ -311,7 +297,7 @@ struct aws_mqtt_protocol_adapter *aws_mqtt_protocol_adapter_new_from_5(struct aw adapter->allocator = allocator; adapter->base.impl = adapter; adapter->base.vtable = &s_protocol_adapter_mqtt5_vtable; - adapter->callback_ref = aws_protocol_adapter_weak_ref_new(allocator, adapter); + adapter->callback_ref = aws_weak_ref_new(allocator, adapter); adapter->config = *options; adapter->loop = client->loop; adapter->client = aws_mqtt5_client_acquire(client); diff --git a/source/request-response/weak_ref.c b/source/request-response/weak_ref.c new file mode 100644 index 00000000..e3e5009c --- /dev/null +++ b/source/request-response/weak_ref.c @@ -0,0 +1,54 @@ +/** +* Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. +* SPDX-License-Identifier: Apache-2.0. +*/ + +#include + +#include + +struct aws_weak_ref { + struct aws_allocator *allocator; + struct aws_ref_count refcount; + void *referenced; +}; + +static void s_destroy_weak_ref(void *value) { + struct aws_weak_ref *weak_ref = value; + + aws_mem_release(weak_ref->allocator, weak_ref); +} + +struct aws_weak_ref *aws_weak_ref_new(struct aws_allocator *allocator, void *referenced) { + struct aws_weak_ref *weak_ref = aws_mem_calloc(allocator, 1, sizeof(struct aws_weak_ref)); + + aws_ref_count_init(&weak_ref->refcount, weak_ref, s_destroy_weak_ref); + weak_ref->allocator = allocator; + weak_ref->referenced = referenced; + + return weak_ref; +} + +struct aws_weak_ref *aws_weak_ref_acquire(struct aws_weak_ref *weak_ref) { + if (NULL != weak_ref) { + aws_ref_count_acquire(&weak_ref->refcount); + } + + return weak_ref; +} + +struct aws_weak_ref *aws_weak_ref_release(struct aws_weak_ref *weak_ref) { + if (NULL != weak_ref) { + aws_ref_count_release(&weak_ref->refcount); + } + + return NULL; +} + +void *aws_weak_ref_get_reference(struct aws_weak_ref *weak_ref) { + return weak_ref->referenced; +} + +void aws_weak_ref_zero_reference(struct aws_weak_ref *weak_ref) { + weak_ref->referenced = NULL; +} \ No newline at end of file From 7a1db2657146dc004484d68f017633061e4c8763 Mon Sep 17 00:00:00 2001 From: Bret Ambrose Date: Wed, 17 Jan 2024 07:03:00 -0800 Subject: [PATCH 03/19] Sync point --- .../request-response/protocol_adapter.h | 4 +- source/request-response/protocol_adapter.c | 101 +++++++++++++----- 2 files changed, 79 insertions(+), 26 deletions(-) diff --git a/include/aws/mqtt/private/request-response/protocol_adapter.h b/include/aws/mqtt/private/request-response/protocol_adapter.h index 7d7d9117..90c4f0c4 100644 --- a/include/aws/mqtt/private/request-response/protocol_adapter.h +++ b/include/aws/mqtt/private/request-response/protocol_adapter.h @@ -63,7 +63,7 @@ struct aws_mqtt_protocol_adapter_options { struct aws_mqtt_protocol_adapter_vtable { - void (*aws_mqtt_protocol_adapter_release_fn)(void *); + void (*aws_mqtt_protocol_adapter_delete_fn)(void *); int (*aws_mqtt_protocol_adapter_subscribe_fn)(void *, struct aws_protocol_adapter_subscribe_options *); @@ -83,7 +83,7 @@ AWS_MQTT_API struct aws_mqtt_protocol_adapter *aws_mqtt_protocol_adapter_new_fro AWS_MQTT_API struct aws_mqtt_protocol_adapter *aws_mqtt_protocol_adapter_new_from_5(struct aws_allocator *allocator, struct aws_mqtt_protocol_adapter_options *options, struct aws_mqtt5_client *client); -AWS_MQTT_API void aws_mqtt_protocol_adapter_release(struct aws_mqtt_protocol_adapter *adapter); +AWS_MQTT_API void aws_mqtt_protocol_adapter_delete(struct aws_mqtt_protocol_adapter *adapter); AWS_MQTT_API int aws_mqtt_protocol_adapter_subscribe(struct aws_mqtt_protocol_adapter *adapter, struct aws_protocol_adapter_subscribe_options *options); diff --git a/source/request-response/protocol_adapter.c b/source/request-response/protocol_adapter.c index 7c91bda2..f3494c86 100644 --- a/source/request-response/protocol_adapter.c +++ b/source/request-response/protocol_adapter.c @@ -43,6 +43,8 @@ static void s_aws_mqtt_protocol_adapter_subscription_destroy(struct aws_mqtt_pro aws_mem_release(subscription->allocator, subscription); } +/******************************************************************************************************************/ + struct aws_mqtt_protocol_adapter_subscription_set { struct aws_allocator *allocator; struct aws_mqtt_protocol_adapter *owner; // not an acquired reference due to the parent-child relationship @@ -90,6 +92,42 @@ static void s_aws_mqtt_protocol_adapter_subscription_set_clean_up(struct aws_mqt aws_hash_table_clean_up(&subscriptions); } +static void s_aws_mqtt_protocol_adapter_subscription_set_update_subscription(struct aws_mqtt_protocol_adapter_subscription_set *subscription_set, struct aws_byte_cursor topic_filter, enum aws_protocol_adapter_subscription_status status) { + (void)subscription_set; + (void)topic_filter; + (void)status; + + // TODO +} + +static void s_aws_mqtt_protocol_adapter_subscription_set_create_or_update_subscription(struct aws_mqtt_protocol_adapter_subscription_set *subscription_set, struct aws_byte_cursor topic_filter, enum aws_protocol_adapter_subscription_status status) { + (void)subscription_set; + (void)topic_filter; + (void)status; + + // TODO +} + +/* + * New API contract + * + * Invariant 1: Subscribe is only called from the RR subscription manager when going from 0 to 1 pending operations + * Invariant 2: Unsubscribe is only called from the RR subscription manager when there are 0 pending operations, not + * necessarily on the exact transition to zero though. + * + * Entries are not tracked with the exception of eventstream impl which needs the stream handles to close. + * A subscribe failure should not trigger an unsubscribe, only notify the status callback. + * Subscription event callback should be {subscribe_success, subscribe_failure, unsubscribe_success, unsubscribe_failure}. + * The sub manager is responsible for calling Unsubscribe on all its entries when shutting down (before releasing + * hold of the adapter). + * + * How do we know not to retry unsubscribe failures because a subscribe came in? Well, we don't retry failures; let + * the manager make that decision. Only retry (maybe) if the manager is gone (ie failure against a zeroed weak ref). + * + * On subscribe failures with zeroed weak ref, trust that an Unsubscribe was sent that will resolve later and let it + * decide what to do. + */ + /* * On subscribe success: if there's not an entry, is this possible? No because we're called only by a function that checks for adapter weak->strong first, so the adapter exists and we don't allow subscription removal without an unsubscribe complete and we don't allow the subscribe until the unsubscribe has completed. But what * if @@ -104,7 +142,9 @@ static void s_aws_mqtt_protocol_adapter_subscription_set_clean_up(struct aws_mqt * is if we're in/post destruction but then there's no adapter and we early out */ static void s_aws_mqtt_protocol_adapter_subscription_set_on_subscribe_completion(struct aws_mqtt_protocol_adapter_subscription_set *subscription_set, struct aws_byte_cursor topic_filter, bool success) { - if (!success) { + if (success) { + s_aws_mqtt_protocol_adapter_subscription_set_update_subscription(subscription_set, topic_filter, PASS_SUBSCRIBED); + } else { struct aws_protocol_adapter_unsubscribe_options options = { .topic_filter = topic_filter, }; @@ -112,32 +152,30 @@ static void s_aws_mqtt_protocol_adapter_subscription_set_on_subscribe_completion aws_mqtt_protocol_adapter_unsubscribe(subscription_set->owner, &options); } + + struct aws_hash_element *hash_element = NULL; if (!aws_hash_table_find(&subscription_set->subscriptions, &topic_filter, &hash_element) || hash_element == NULL) { return; } struct aws_mqtt_protocol_adapter_subscription *subscription = hash_element->value; + AWS_FATAL_ASSERT(subscription != NULL); -} - -static void s_aws_mqtt_protocol_adapter_subscription_set_update_subscription(struct aws_mqtt_protocol_adapter_subscription_set *subscription_set, struct aws_byte_cursor topic_filter, enum aws_protocol_adapter_subscription_status status) { - (void)subscription_set; - (void)topic_filter; - (void)status; + switch (subscription->status) { + case PASS_SUBSCRIBING: { + if (success) { + subscription->status = PASS_SUBSCRIBED; + } + } - ??; -} - -static void s_aws_mqtt_protocol_adapter_subscription_set_create_or_update_subscription(struct aws_mqtt_protocol_adapter_subscription_set *subscription_set, struct aws_byte_cursor topic_filter, enum aws_protocol_adapter_subscription_status status) { - (void)subscription_set; - (void)topic_filter; - (void)status; + default: + break; + } - ??; + // TODO } - /******************************************************************************************************************/ struct aws_mqtt_protocol_adapter *aws_mqtt_protocol_adapter_new_from_311(struct aws_allocator *allocator, struct aws_mqtt_protocol_adapter_options *options, struct aws_mqtt_client_connection *connection) { @@ -145,6 +183,7 @@ struct aws_mqtt_protocol_adapter *aws_mqtt_protocol_adapter_new_from_311(struct (void)options; (void)connection; + // TODO return NULL; } @@ -163,16 +202,18 @@ struct aws_mqtt_protocol_adapter_5_impl { struct aws_mqtt_protocol_adapter_subscription_set subscriptions; }; -static void s_aws_mqtt_protocol_adapter_5_release(void *impl) { +static void s_aws_mqtt_protocol_adapter_5_delete(void *impl) { struct aws_mqtt_protocol_adapter_5_impl *adapter = impl; aws_mqtt5_listener_release(adapter->listener); } +/* Subscribe */ + struct aws_mqtt_protocol_adapter_5_subscribe_data { struct aws_allocator *allocator; - struct aws_byte_buf *topic_filter; + struct aws_byte_buf topic_filter; struct aws_protocol_adapter_weak_ref *callback_ref; }; @@ -188,6 +229,7 @@ static struct aws_mqtt_protocol_adapter_5_subscribe_data *aws_mqtt_protocol_adap static void aws_mqtt_protocol_adapter_5_subscribe_data_delete(struct aws_mqtt_protocol_adapter_5_subscribe_data *subscribe_data) { aws_weak_ref_release(subscribe_data->callback_ref); + aws_byte_buf_clean_up(&subscribe_data->topic_filter); aws_mem_release(subscribe_data->allocator, subscribe_data); } @@ -202,7 +244,7 @@ static void s_protocol_adapter_5_subscribe_completion(const struct aws_mqtt5_pac goto done; } - bool success = error_code == AWS_ERROR_SUCCESS && suback != NULL && suback->reason_code_count == 1 && suback->reason_codes[0] <= AWS_MQTT5_SARC_GRANTED_QOS_1; + bool success = error_code == AWS_ERROR_SUCCESS && suback != NULL && suback->reason_code_count == 1 && suback->reason_codes[0] <= AWS_MQTT5_SARC_GRANTED_QOS_2; s_aws_mqtt_protocol_adapter_subscription_set_on_subscribe_completion(&adapter->subscriptions, aws_byte_cursor_from_buf(&subscribe_data->topic_filter), success); done: @@ -244,6 +286,8 @@ int s_aws_mqtt_protocol_adapter_5_subscribe(void *impl, struct aws_protocol_adap return AWS_OP_ERR; } +/* Unsubscribe */ + int s_aws_mqtt_protocol_adapter_5_unsubscribe(void *impl, struct aws_protocol_adapter_unsubscribe_options *options) { struct aws_mqtt_protocol_adapter_5_impl *adapter = impl; (void)adapter; @@ -252,6 +296,8 @@ int s_aws_mqtt_protocol_adapter_5_unsubscribe(void *impl, struct aws_protocol_ad return aws_raise_error(AWS_ERROR_UNIMPLEMENTED); } +/* Publish */ + int s_aws_mqtt_protocol_adapter_5_publish(void *impl, struct aws_protocol_adapter_publish_options *options) { struct aws_mqtt_protocol_adapter_5_impl *adapter = impl; (void)adapter; @@ -274,16 +320,23 @@ static void s_protocol_adapter_mqtt5_listener_termination_callback(void *user_da AWS_FATAL_ASSERT(aws_event_loop_thread_is_callers_thread(adapter->client->loop)); - aws_mqtt5_client_release(adapter->client); - aws_weak_ref_zero_reference(adapter->callback_ref); aws_weak_ref_release(adapter->callback_ref); + aws_mqtt5_client_release(adapter->client); + + aws_protocol_adapter_terminate_callback_fn *terminate_callback = adapter->config.terminate_callback; + void *terminate_user_data = adapter->config.user_data; + aws_mem_release(adapter->allocator, adapter); + + if (terminate_callback) { + (*terminate_callback)(terminate_user_data); + } } static struct aws_mqtt_protocol_adapter_vtable s_protocol_adapter_mqtt5_vtable = { - .aws_mqtt_protocol_adapter_release_fn = s_aws_mqtt_protocol_adapter_5_release, + .aws_mqtt_protocol_adapter_delete_fn = s_aws_mqtt_protocol_adapter_5_delete, .aws_mqtt_protocol_adapter_subscribe_fn = s_aws_mqtt_protocol_adapter_5_subscribe, .aws_mqtt_protocol_adapter_unsubscribe_fn = s_aws_mqtt_protocol_adapter_5_unsubscribe, .aws_mqtt_protocol_adapter_publish_fn = s_aws_mqtt_protocol_adapter_5_publish, @@ -319,8 +372,8 @@ struct aws_mqtt_protocol_adapter *aws_mqtt_protocol_adapter_new_from_5(struct aw return adapter; } -void aws_mqtt_protocol_adapter_release(struct aws_mqtt_protocol_adapter *adapter) { - (*adapter->vtable->aws_mqtt_protocol_adapter_release_fn)(adapter->impl); +void aws_mqtt_protocol_adapter_delete(struct aws_mqtt_protocol_adapter *adapter) { + (*adapter->vtable->aws_mqtt_protocol_adapter_delete_fn)(adapter->impl); } int aws_mqtt_protocol_adapter_subscribe(struct aws_mqtt_protocol_adapter *adapter, struct aws_protocol_adapter_subscribe_options *options) { From 013168bc598d57f345ea76eae8503645aca68194 Mon Sep 17 00:00:00 2001 From: Bret Ambrose Date: Wed, 17 Jan 2024 17:26:29 -0800 Subject: [PATCH 04/19] Checkpoint --- .../request-response/protocol_adapter.h | 31 +- source/request-response/protocol_adapter.c | 340 +++++++++--------- tests/CMakeLists.txt | 5 + tests/v5/mqtt5_client_tests.c | 28 -- tests/v5/mqtt5_testing_utils.c | 26 ++ tests/v5/mqtt5_testing_utils.h | 5 + .../request_response_protocol_adapter_tests.c | 133 +++++++ 7 files changed, 363 insertions(+), 205 deletions(-) create mode 100644 tests/v5/request_response_protocol_adapter_tests.c diff --git a/include/aws/mqtt/private/request-response/protocol_adapter.h b/include/aws/mqtt/private/request-response/protocol_adapter.h index 90c4f0c4..3cf39a07 100644 --- a/include/aws/mqtt/private/request-response/protocol_adapter.h +++ b/include/aws/mqtt/private/request-response/protocol_adapter.h @@ -28,20 +28,21 @@ struct aws_protocol_adapter_publish_options { struct aws_byte_cursor topic; struct aws_byte_cursor payload; - void (*completion_callback_fn)(int, void *); + void (*completion_callback_fn)(bool, void *); void *user_data; uint32_t ack_timeout_seconds; }; -enum aws_protocol_adapter_subscription_status_update { - AWS_PASS_ESTABLISHMENT_SUCCESS, - AWS_PASS_ESTABLISHMENT_FAILURE, - AWS_PASS_REMOVED +enum aws_protocol_adapter_subscription_event_type { + AWS_PASET_SUBSCRIBE_SUCCESS, + AWS_PASET_SUBSCRIBE_FAILURE, + AWS_PASET_UNSUBSCRIBE_SUCCESS, + AWS_PASET_UNSUBSCRIBE_FAILURE, }; -struct aws_protocol_adapter_subscription_status_update_event { +struct aws_protocol_adapter_subscription_event { struct aws_byte_cursor topic_filter; - enum aws_protocol_adapter_subscription_status_update status_update; + enum aws_protocol_adapter_subscription_event_type event_type; }; struct aws_protocol_adapter_incoming_publish_event { @@ -49,14 +50,26 @@ struct aws_protocol_adapter_incoming_publish_event { struct aws_byte_cursor payload; }; -typedef void(aws_protocol_adapter_subscription_status_fn)(struct aws_protocol_adapter_subscription_status_update_event *update, void *user_data); +enum aws_protocol_adapter_connection_event_type { + AWS_PACET_OFFLINE, + AWS_PACET_ONLINE, +}; + +struct aws_protocol_adapter_connection_event { + enum aws_protocol_adapter_connection_event_type event_type; + bool rejoined_session; +}; + +typedef void(aws_protocol_adapter_subscription_event_fn)(struct aws_protocol_adapter_subscription_event *event, void *user_data); typedef void(aws_protocol_adapter_incoming_publish_fn)(struct aws_protocol_adapter_incoming_publish_event *publish, void *user_data); typedef void(aws_protocol_adapter_terminate_callback_fn)(void *user_data); +typedef void(aws_protocol_adapter_connection_event_fn)(struct aws_protocol_adapter_connection_event *event, void *user_data); struct aws_mqtt_protocol_adapter_options { - aws_protocol_adapter_subscription_status_fn *subscription_status_update_callback; + aws_protocol_adapter_subscription_event_fn *subscription_event_callback; aws_protocol_adapter_incoming_publish_fn *incoming_publish_callback; aws_protocol_adapter_terminate_callback_fn *terminate_callback; + aws_protocol_adapter_connection_event_fn *connection_event_callback; void *user_data; }; diff --git a/source/request-response/protocol_adapter.c b/source/request-response/protocol_adapter.c index f3494c86..4aae75cd 100644 --- a/source/request-response/protocol_adapter.c +++ b/source/request-response/protocol_adapter.c @@ -12,102 +12,6 @@ #include #include -enum aws_protocol_adapter_subscription_status { - PASS_NONE, - PASS_SUBSCRIBING, - PASS_SUBSCRIBED, - PASS_UNSUBSCRIBING, -}; - -struct aws_mqtt_protocol_adapter_subscription { - struct aws_allocator *allocator; - - struct aws_byte_cursor topic_filter; - struct aws_byte_buf topic_filter_buf; - - enum aws_protocol_adapter_subscription_status status; -}; - -static struct aws_mqtt_protocol_adapter_subscription *s_aws_mqtt_protocol_adapter_subscription_new(struct aws_allocator *allocator, struct aws_byte_cursor topic_filter) { - struct aws_mqtt_protocol_adapter_subscription *subscription = aws_mem_calloc(allocator, 1, sizeof(struct aws_mqtt_protocol_adapter_subscription)); - - subscription->allocator = allocator; - aws_byte_buf_init_copy_from_cursor(&subscription->topic_filter_buf, allocator, topic_filter); - subscription->topic_filter = aws_byte_cursor_from_buf(&subscription->topic_filter_buf); - - return subscription; -} - -static void s_aws_mqtt_protocol_adapter_subscription_destroy(struct aws_mqtt_protocol_adapter_subscription *subscription) { - aws_byte_buf_clean_up(&subscription->topic_filter_buf); - aws_mem_release(subscription->allocator, subscription); -} - -/******************************************************************************************************************/ - -struct aws_mqtt_protocol_adapter_subscription_set { - struct aws_allocator *allocator; - struct aws_mqtt_protocol_adapter *owner; // not an acquired reference due to the parent-child relationship - struct aws_hash_table subscriptions; // aws_byte_cursor * -> aws_mqtt_protocol_adapter_subscription * - - aws_protocol_adapter_subscription_status_fn *subscription_status_update_callback; - void *callback_user_data; -}; - -static int s_aws_mqtt_protocol_adapter_subscription_set_init(struct aws_mqtt_protocol_adapter_subscription_set *subscription_set, struct aws_allocator *allocator, struct aws_mqtt_protocol_adapter *owner, struct aws_mqtt_protocol_adapter_options *options) { - subscription_set->allocator = allocator; - subscription_set->owner = owner; - subscription_set->subscription_status_update_callback = options->subscription_status_update_callback; - subscription_set->callback_user_data = options->user_data; - - return aws_hash_table_init(&subscription_set->subscriptions, allocator, 0, aws_hash_byte_cursor_ptr, aws_mqtt_byte_cursor_hash_equality, NULL, NULL); -} - -static int s_aws_mqtt_protocol_adapter_subscription_set_subscription_destroy(void *context, struct aws_hash_element *elem) { - struct aws_mqtt_protocol_adapter_subscription_set *subscription_set = context; - struct aws_mqtt_protocol_adapter *adapter = subscription_set->owner; - - struct aws_mqtt_protocol_adapter_subscription *subscription = elem->value; - if (subscription->status != PASS_UNSUBSCRIBING) { - struct aws_protocol_adapter_unsubscribe_options options = { - .topic_filter = subscription->topic_filter, - }; - - aws_mqtt_protocol_adapter_unsubscribe(adapter, &options); - } - - s_aws_mqtt_protocol_adapter_subscription_destroy(subscription); - - return AWS_COMMON_HASH_TABLE_ITER_CONTINUE | AWS_COMMON_HASH_TABLE_ITER_DELETE; -} - -static void s_aws_mqtt_protocol_adapter_subscription_set_clean_up(struct aws_mqtt_protocol_adapter_subscription_set *subscription_set) { - struct aws_hash_table subscriptions; - AWS_ZERO_STRUCT(subscriptions); - - aws_hash_table_swap(&subscription_set->subscriptions, &subscriptions); - - aws_hash_table_foreach(&subscriptions, s_aws_mqtt_protocol_adapter_subscription_set_subscription_destroy, subscription_set); - - aws_hash_table_clean_up(&subscriptions); -} - -static void s_aws_mqtt_protocol_adapter_subscription_set_update_subscription(struct aws_mqtt_protocol_adapter_subscription_set *subscription_set, struct aws_byte_cursor topic_filter, enum aws_protocol_adapter_subscription_status status) { - (void)subscription_set; - (void)topic_filter; - (void)status; - - // TODO -} - -static void s_aws_mqtt_protocol_adapter_subscription_set_create_or_update_subscription(struct aws_mqtt_protocol_adapter_subscription_set *subscription_set, struct aws_byte_cursor topic_filter, enum aws_protocol_adapter_subscription_status status) { - (void)subscription_set; - (void)topic_filter; - (void)status; - - // TODO -} - /* * New API contract * @@ -122,61 +26,13 @@ static void s_aws_mqtt_protocol_adapter_subscription_set_create_or_update_subscr * hold of the adapter). * * How do we know not to retry unsubscribe failures because a subscribe came in? Well, we don't retry failures; let - * the manager make that decision. Only retry (maybe) if the manager is gone (ie failure against a zeroed weak ref). + * the manager make that decision. No retry when the weak ref is zeroed either. The potential for things to go wrong + * is worse than the potential of a subscription "leaking." * * On subscribe failures with zeroed weak ref, trust that an Unsubscribe was sent that will resolve later and let it * decide what to do. */ -/* - * On subscribe success: if there's not an entry, is this possible? No because we're called only by a function that checks for adapter weak->strong first, so the adapter exists and we don't allow subscription removal without an unsubscribe complete and we don't allow the subscribe until the unsubscribe has completed. But what - * if - * On subscribe success: if there's an entry, transition | subscribing -> subscribed, send an update - * On subscribe failure: if there's not an entry, is this possible? - * On subscribe failure: if there's an entry, transition -> unsubscribing, send an update - * - * Should we just blindly add if the adapter exists? Yes: simplest. No: represents undefined behavior if it shouldn't be happening - * - * In the design we said that the subscription set is just a dumb reflection of the ordered sequence of operations - * from the rr client which implies we should just create_or_update. The only time we don't want to create_or_update - * is if we're in/post destruction but then there's no adapter and we early out - */ -static void s_aws_mqtt_protocol_adapter_subscription_set_on_subscribe_completion(struct aws_mqtt_protocol_adapter_subscription_set *subscription_set, struct aws_byte_cursor topic_filter, bool success) { - if (success) { - s_aws_mqtt_protocol_adapter_subscription_set_update_subscription(subscription_set, topic_filter, PASS_SUBSCRIBED); - } else { - struct aws_protocol_adapter_unsubscribe_options options = { - .topic_filter = topic_filter, - }; - - aws_mqtt_protocol_adapter_unsubscribe(subscription_set->owner, &options); - } - - - - struct aws_hash_element *hash_element = NULL; - if (!aws_hash_table_find(&subscription_set->subscriptions, &topic_filter, &hash_element) || hash_element == NULL) { - return; - } - - struct aws_mqtt_protocol_adapter_subscription *subscription = hash_element->value; - AWS_FATAL_ASSERT(subscription != NULL); - - switch (subscription->status) { - case PASS_SUBSCRIBING: { - if (success) { - subscription->status = PASS_SUBSCRIBED; - } - } - - default: - break; - } - - // TODO -} - -/******************************************************************************************************************/ struct aws_mqtt_protocol_adapter *aws_mqtt_protocol_adapter_new_from_311(struct aws_allocator *allocator, struct aws_mqtt_protocol_adapter_options *options, struct aws_mqtt_client_connection *connection) { (void)allocator; @@ -198,27 +54,25 @@ struct aws_mqtt_protocol_adapter_5_impl { struct aws_event_loop *loop; struct aws_mqtt5_client *client; struct aws_mqtt5_listener *listener; - - struct aws_mqtt_protocol_adapter_subscription_set subscriptions; }; static void s_aws_mqtt_protocol_adapter_5_delete(void *impl) { struct aws_mqtt_protocol_adapter_5_impl *adapter = impl; + // all the real cleanup is done in the listener termination callback aws_mqtt5_listener_release(adapter->listener); } -/* Subscribe */ - -struct aws_mqtt_protocol_adapter_5_subscribe_data { +// used by both subscribe and unsubscribe +struct aws_mqtt_protocol_adapter_5_subscription_op_data { struct aws_allocator *allocator; struct aws_byte_buf topic_filter; struct aws_protocol_adapter_weak_ref *callback_ref; }; -static struct aws_mqtt_protocol_adapter_5_subscribe_data *aws_mqtt_protocol_adapter_5_subscribe_data_new(struct aws_allocator *allocator, struct aws_byte_cursor topic_filter, struct aws_protocol_adapter_weak_ref *callback_ref) { - struct aws_mqtt_protocol_adapter_5_subscribe_data *subscribe_data = aws_mem_calloc(allocator, 1, sizeof(struct aws_mqtt_protocol_adapter_5_subscribe_data)); +static struct aws_mqtt_protocol_adapter_5_subscription_op_data *s_aws_mqtt_protocol_adapter_5_subscription_op_data_new(struct aws_allocator *allocator, struct aws_byte_cursor topic_filter, struct aws_protocol_adapter_weak_ref *callback_ref) { + struct aws_mqtt_protocol_adapter_5_subscription_op_data *subscribe_data = aws_mem_calloc(allocator, 1, sizeof(struct aws_mqtt_protocol_adapter_5_subscription_op_data)); subscribe_data->allocator = allocator; subscribe_data->callback_ref = aws_weak_ref_acquire(callback_ref); @@ -227,17 +81,19 @@ static struct aws_mqtt_protocol_adapter_5_subscribe_data *aws_mqtt_protocol_adap return subscribe_data; } -static void aws_mqtt_protocol_adapter_5_subscribe_data_delete(struct aws_mqtt_protocol_adapter_5_subscribe_data *subscribe_data) { +static void s_aws_mqtt_protocol_adapter_5_subscription_op_data_delete(struct aws_mqtt_protocol_adapter_5_subscription_op_data *subscribe_data) { aws_weak_ref_release(subscribe_data->callback_ref); aws_byte_buf_clean_up(&subscribe_data->topic_filter); aws_mem_release(subscribe_data->allocator, subscribe_data); } +/* Subscribe */ + static void s_protocol_adapter_5_subscribe_completion(const struct aws_mqtt5_packet_suback_view *suback, int error_code, void *complete_ctx) { - struct aws_mqtt_protocol_adapter_5_subscribe_data *subscribe_data = complete_ctx; + struct aws_mqtt_protocol_adapter_5_subscription_op_data *subscribe_data = complete_ctx; struct aws_mqtt_protocol_adapter_5_impl *adapter = aws_weak_ref_get_reference(subscribe_data->callback_ref); if (adapter == NULL) { @@ -245,17 +101,23 @@ static void s_protocol_adapter_5_subscribe_completion(const struct aws_mqtt5_pac } bool success = error_code == AWS_ERROR_SUCCESS && suback != NULL && suback->reason_code_count == 1 && suback->reason_codes[0] <= AWS_MQTT5_SARC_GRANTED_QOS_2; - s_aws_mqtt_protocol_adapter_subscription_set_on_subscribe_completion(&adapter->subscriptions, aws_byte_cursor_from_buf(&subscribe_data->topic_filter), success); + + struct aws_protocol_adapter_subscription_event subscribe_event = { + .topic_filter = aws_byte_cursor_from_buf(&subscribe_data->topic_filter), + .event_type = success ? AWS_PASET_SUBSCRIBE_SUCCESS : AWS_PASET_SUBSCRIBE_FAILURE, + }; + + (*adapter->config.subscription_event_callback)(&subscribe_event, adapter->config.user_data); done: - aws_mqtt_protocol_adapter_5_subscribe_data_delete(subscribe_data); + s_aws_mqtt_protocol_adapter_5_subscription_op_data_delete(subscribe_data); } int s_aws_mqtt_protocol_adapter_5_subscribe(void *impl, struct aws_protocol_adapter_subscribe_options *options) { struct aws_mqtt_protocol_adapter_5_impl *adapter = impl; - struct aws_mqtt_protocol_adapter_5_subscribe_data *subscribe_data = aws_mqtt_protocol_adapter_5_subscribe_data_new(adapter->allocator, options->topic_filter, adapter->callback_ref); + struct aws_mqtt_protocol_adapter_5_subscription_op_data *subscribe_data = s_aws_mqtt_protocol_adapter_5_subscription_op_data_new(adapter->allocator, options->topic_filter, adapter->callback_ref); struct aws_mqtt5_subscription_view subscription_view = { .qos = AWS_MQTT5_QOS_AT_LEAST_ONCE, @@ -281,38 +143,180 @@ int s_aws_mqtt_protocol_adapter_5_subscribe(void *impl, struct aws_protocol_adap error: - aws_mqtt_protocol_adapter_5_subscribe_data_delete(subscribe_data); + s_aws_mqtt_protocol_adapter_5_subscription_op_data_delete(subscribe_data); return AWS_OP_ERR; } /* Unsubscribe */ +static void s_protocol_adapter_5_unsubscribe_completion(const struct aws_mqtt5_packet_unsuback_view *unsuback, + int error_code, + void *complete_ctx) { + struct aws_mqtt_protocol_adapter_5_subscription_op_data *unsubscribe_data = complete_ctx; + struct aws_mqtt_protocol_adapter_5_impl *adapter = aws_weak_ref_get_reference(unsubscribe_data->callback_ref); + + if (adapter == NULL) { + goto done; + } + + bool success = error_code == AWS_ERROR_SUCCESS && unsuback != NULL && unsuback->reason_code_count == 1 && unsuback->reason_codes[0] < 128; + + struct aws_protocol_adapter_subscription_event unsubscribe_event = { + .topic_filter = aws_byte_cursor_from_buf(&unsubscribe_data->topic_filter), + .event_type = success ? AWS_PASET_UNSUBSCRIBE_SUCCESS : AWS_PASET_UNSUBSCRIBE_FAILURE, + }; + + (*adapter->config.subscription_event_callback)(&unsubscribe_event, adapter->config.user_data); + +done: + + s_aws_mqtt_protocol_adapter_5_subscription_op_data_delete(unsubscribe_data); +} + int s_aws_mqtt_protocol_adapter_5_unsubscribe(void *impl, struct aws_protocol_adapter_unsubscribe_options *options) { struct aws_mqtt_protocol_adapter_5_impl *adapter = impl; - (void)adapter; - (void)options; - return aws_raise_error(AWS_ERROR_UNIMPLEMENTED); + struct aws_mqtt_protocol_adapter_5_subscription_op_data *unsubscribe_data = s_aws_mqtt_protocol_adapter_5_subscription_op_data_new(adapter->allocator, options->topic_filter, adapter->callback_ref); + + struct aws_mqtt5_packet_unsubscribe_view unsubscribe_view = { + .topic_filters = &options->topic_filter, + .topic_filter_count = 1, + }; + + struct aws_mqtt5_unsubscribe_completion_options completion_options = { + .ack_timeout_seconds_override = options->ack_timeout_seconds, + .completion_callback = s_protocol_adapter_5_unsubscribe_completion, + .completion_user_data = unsubscribe_data, + }; + + if (aws_mqtt5_client_unsubscribe(adapter->client, &unsubscribe_view, &completion_options)) { + goto error; + } + + return AWS_OP_SUCCESS; + +error: + + s_aws_mqtt_protocol_adapter_5_subscription_op_data_delete(unsubscribe_data); + + return AWS_OP_ERR; } /* Publish */ +struct aws_mqtt_protocol_adapter_5_publish_op_data { + struct aws_allocator *allocator; + struct aws_protocol_adapter_weak_ref *callback_ref; + + void (*completion_callback_fn)(bool, void *); + void *user_data; +}; + +static struct aws_mqtt_protocol_adapter_5_publish_op_data *s_aws_mqtt_protocol_adapter_5_publish_op_data_new(struct aws_allocator *allocator, const struct aws_protocol_adapter_publish_options *publish_options, struct aws_protocol_adapter_weak_ref *callback_ref) { + struct aws_mqtt_protocol_adapter_5_publish_op_data *publish_data = aws_mem_calloc(allocator, 1, sizeof(struct aws_mqtt_protocol_adapter_5_publish_op_data)); + + publish_data->allocator = allocator; + publish_data->callback_ref = aws_weak_ref_acquire(callback_ref); + publish_data->completion_callback_fn = publish_options->completion_callback_fn; + publish_data->user_data = publish_options->user_data; + + return publish_data; +} + +static void s_aws_mqtt_protocol_adapter_5_publish_op_data_delete(struct aws_mqtt_protocol_adapter_5_publish_op_data *publish_data) { + aws_weak_ref_release(publish_data->callback_ref); + + aws_mem_release(publish_data->allocator, publish_data); +} + +static void s_protocol_adapter_5_publish_completion( + enum aws_mqtt5_packet_type packet_type, + const void *packet, + int error_code, + void *complete_ctx) { + struct aws_mqtt_protocol_adapter_5_publish_op_data *publish_data = complete_ctx; + struct aws_mqtt_protocol_adapter_5_impl *adapter = aws_weak_ref_get_reference(publish_data->callback_ref); + + if (adapter == NULL) { + goto done; + } + + bool success = false; + if (error_code == AWS_ERROR_SUCCESS && packet_type == AWS_MQTT5_PT_PUBACK) { + const struct aws_mqtt5_packet_puback_view *puback = packet; + if (puback->reason_code < 128) { + success = true; + } + } + + (*publish_data->completion_callback_fn)(success, publish_data->user_data); + +done: + + s_aws_mqtt_protocol_adapter_5_publish_op_data_delete(publish_data); +} + int s_aws_mqtt_protocol_adapter_5_publish(void *impl, struct aws_protocol_adapter_publish_options *options) { struct aws_mqtt_protocol_adapter_5_impl *adapter = impl; - (void)adapter; - (void)options; + struct aws_mqtt_protocol_adapter_5_publish_op_data *publish_data = s_aws_mqtt_protocol_adapter_5_publish_op_data_new(adapter->allocator, options, adapter->callback_ref); - return aws_raise_error(AWS_ERROR_UNIMPLEMENTED); + struct aws_mqtt5_packet_publish_view publish_view = { + .topic = options->topic, + .qos = AWS_MQTT5_QOS_AT_LEAST_ONCE, + .payload = options->payload + }; + + struct aws_mqtt5_publish_completion_options completion_options = { + .ack_timeout_seconds_override = options->ack_timeout_seconds, + .completion_callback = s_protocol_adapter_5_publish_completion, + .completion_user_data = publish_data, + }; + + if (aws_mqtt5_client_publish(adapter->client, &publish_view, &completion_options)) { + goto error; + } + + return AWS_OP_SUCCESS; + +error: + + s_aws_mqtt_protocol_adapter_5_publish_op_data_delete(publish_data); + + return AWS_OP_ERR; } static bool s_protocol_adapter_mqtt5_listener_publish_received(const struct aws_mqtt5_packet_publish_view *publish, void *user_data) { - (void)publish; - (void)user_data; + struct aws_mqtt_protocol_adapter_5_impl *adapter = user_data; + + struct aws_protocol_adapter_incoming_publish_event publish_event = { + .topic = publish->topic, + .payload = publish->payload + }; + + (*adapter->config.incoming_publish_callback)(&publish_event, adapter->config.user_data); + + return false; } static void s_protocol_adapter_mqtt5_lifecycle_event_callback(const struct aws_mqtt5_client_lifecycle_event *event) { - (void)event; + struct aws_mqtt_protocol_adapter_5_impl *adapter = event->user_data; + + if (event->event_type != AWS_MQTT5_CLET_CONNECTION_SUCCESS && event->event_type != AWS_MQTT5_CLET_DISCONNECTION) { + return; + } + + bool is_connection_success = event->event_type == AWS_MQTT5_CLET_CONNECTION_SUCCESS; + + struct aws_protocol_adapter_connection_event connection_event = { + .event_type = is_connection_success ? AWS_PACET_ONLINE : AWS_PACET_OFFLINE, + }; + + if (is_connection_success) { + connection_event.rejoined_session = event->settings->rejoined_session; + } + + (*adapter->config.connection_event_callback)(&connection_event, adapter->config.user_data); } static void s_protocol_adapter_mqtt5_listener_termination_callback(void *user_data) { diff --git a/tests/CMakeLists.txt b/tests/CMakeLists.txt index b8ef2587..f1d90e22 100644 --- a/tests/CMakeLists.txt +++ b/tests/CMakeLists.txt @@ -442,6 +442,11 @@ add_test_case(mqtt_subscription_set_publish_single_level_wildcards) add_test_case(mqtt_subscription_set_publish_multi_level_wildcards) add_test_case(mqtt_subscription_set_get_subscriptions) +add_test_case(request_response_mqtt5_protocol_adapter_subscribe_success) +#add_test_case(request_response_mqtt5_protocol_adapter_subscribe_failure_error_code) +#add_test_case(request_response_mqtt5_protocol_adapter_subscribe_failure_reason_code) +#add_test_case(request_response_mqtt5_protocol_adapter_subscribe_failure_timeout) + generate_test_driver(${PROJECT_NAME}-tests) set(TEST_PAHO_CLIENT_BINARY_NAME ${PROJECT_NAME}-paho-client) diff --git a/tests/v5/mqtt5_client_tests.c b/tests/v5/mqtt5_client_tests.c index 2d436f85..06367d3d 100644 --- a/tests/v5/mqtt5_client_tests.c +++ b/tests/v5/mqtt5_client_tests.c @@ -10,7 +10,6 @@ #include #include #include -#include #include #include #include @@ -18,40 +17,13 @@ #include -#include #include -#define TEST_IO_MESSAGE_LENGTH 4096 - static bool s_is_within_percentage_of(uint64_t expected_time, uint64_t actual_time, double percentage) { double actual_percent = 1.0 - (double)actual_time / (double)expected_time; return fabs(actual_percent) <= percentage; } -int aws_mqtt5_mock_server_send_packet( - struct aws_mqtt5_server_mock_connection_context *connection, - enum aws_mqtt5_packet_type packet_type, - void *packet) { - aws_mqtt5_encoder_append_packet_encoding(&connection->encoder, packet_type, packet); - - struct aws_io_message *message = aws_channel_acquire_message_from_pool( - connection->slot->channel, AWS_IO_MESSAGE_APPLICATION_DATA, TEST_IO_MESSAGE_LENGTH); - if (message == NULL) { - return AWS_OP_ERR; - } - - enum aws_mqtt5_encoding_result result = - aws_mqtt5_encoder_encode_to_buffer(&connection->encoder, &message->message_data); - AWS_FATAL_ASSERT(result == AWS_MQTT5_ER_FINISHED); - - if (aws_channel_slot_send_message(connection->slot, message, AWS_CHANNEL_DIR_WRITE)) { - aws_mem_release(message->allocator, message); - return AWS_OP_ERR; - } - - return AWS_OP_SUCCESS; -} - int aws_mqtt5_mock_server_handle_connect_always_succeed( void *packet, struct aws_mqtt5_server_mock_connection_context *connection, diff --git a/tests/v5/mqtt5_testing_utils.c b/tests/v5/mqtt5_testing_utils.c index e8e72f3f..4b682d83 100644 --- a/tests/v5/mqtt5_testing_utils.c +++ b/tests/v5/mqtt5_testing_utils.c @@ -1740,3 +1740,29 @@ size_t aws_mqtt5_linked_list_length(struct aws_linked_list *list) { return length; } + +#define TEST_IO_MESSAGE_LENGTH 4096 + +int aws_mqtt5_mock_server_send_packet( + struct aws_mqtt5_server_mock_connection_context *connection, + enum aws_mqtt5_packet_type packet_type, + void *packet) { + aws_mqtt5_encoder_append_packet_encoding(&connection->encoder, packet_type, packet); + + struct aws_io_message *message = aws_channel_acquire_message_from_pool( + connection->slot->channel, AWS_IO_MESSAGE_APPLICATION_DATA, TEST_IO_MESSAGE_LENGTH); + if (message == NULL) { + return AWS_OP_ERR; + } + + enum aws_mqtt5_encoding_result result = + aws_mqtt5_encoder_encode_to_buffer(&connection->encoder, &message->message_data); + AWS_FATAL_ASSERT(result == AWS_MQTT5_ER_FINISHED); + + if (aws_channel_slot_send_message(connection->slot, message, AWS_CHANNEL_DIR_WRITE)) { + aws_mem_release(message->allocator, message); + return AWS_OP_ERR; + } + + return AWS_OP_SUCCESS; +} \ No newline at end of file diff --git a/tests/v5/mqtt5_testing_utils.h b/tests/v5/mqtt5_testing_utils.h index e4fa6de8..0e30f246 100644 --- a/tests/v5/mqtt5_testing_utils.h +++ b/tests/v5/mqtt5_testing_utils.h @@ -218,6 +218,11 @@ int aws_mqtt5_mock_server_handle_unsubscribe_unsuback_success( struct aws_mqtt5_server_mock_connection_context *connection, void *user_data); +int aws_mqtt5_mock_server_send_packet( + struct aws_mqtt5_server_mock_connection_context *connection, + enum aws_mqtt5_packet_type packet_type, + void *packet); + extern const struct aws_string *g_default_client_id; #define RECONNECT_TEST_MIN_BACKOFF 500 diff --git a/tests/v5/request_response_protocol_adapter_tests.c b/tests/v5/request_response_protocol_adapter_tests.c new file mode 100644 index 00000000..29abcd99 --- /dev/null +++ b/tests/v5/request_response_protocol_adapter_tests.c @@ -0,0 +1,133 @@ +/** +* Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. +* SPDX-License-Identifier: Apache-2.0. +*/ + +#include "mqtt5_testing_utils.h" +#include + +struct request_response_protocol_adapter_incoming_publish_event_record { + struct aws_byte_buf topic; + struct aws_byte_buf payload; +}; + +static void s_request_response_protocol_adapter_incoming_publish_event_record_init( + struct request_response_protocol_adapter_incoming_publish_event_record *record, + struct aws_allocator *allocator, + struct aws_byte_cursor topic, + struct aws_byte_cursor payload) { + + aws_byte_buf_init_copy_from_cursor(&record->topic, allocator, topic); + aws_byte_buf_init_copy_from_cursor(&record->payload, allocator, payload); +} + +static void s_request_response_protocol_adapter_incoming_publish_event_record_cleanup(struct request_response_protocol_adapter_incoming_publish_event_record *record) { + aws_byte_buf_clean_up(&record->topic); + aws_byte_buf_clean_up(&record->payload); +} + +struct request_response_protocol_adapter_connection_event_record { + enum aws_protocol_adapter_connection_event_type event_type; + bool rejoined_session; +}; + +struct request_response_protocol_adapter_subscription_event_record { + enum aws_protocol_adapter_subscription_event_type event_type; + struct aws_byte_buf topic_filter; +}; + +static void s_request_response_protocol_adapter_incoming_subscription_event_record_init( + struct request_response_protocol_adapter_subscription_event_record *record, + struct aws_allocator *allocator, + struct aws_byte_cursor topic_filter) { + + aws_byte_buf_init_copy_from_cursor(&record->topic_filter, allocator, topic_filter); +} + +static void s_request_response_protocol_adapter_incoming_subscription_event_record_cleanup(struct request_response_protocol_adapter_subscription_event_record *record) { + aws_byte_buf_clean_up(&record->topic_filter); +} + +struct aws_request_response_mqtt5_adapter_test_fixture { + struct aws_allocator *allocator; + struct aws_mqtt5_client_mock_test_fixture mqtt5_fixture; + + struct aws_mqtt_protocol_adapter *protocol_adapter; + + struct aws_array_list incoming_publish_events; + struct aws_array_list connection_events; + struct aws_array_list subscription_events; + + struct aws_mutex lock; + struct aws_condition_variable signal; +}; + + +static void s_rr_mqtt5_protocol_adapter_subscription_event(struct aws_protocol_adapter_subscription_event *event, void *user_data) { + struct aws_request_response_mqtt5_adapter_test_fixture *fixture = user_data; + + struct request_response_protocol_adapter_subscription_event_record record = { + .event_type = event->event_type + }; + s_request_response_protocol_adapter_incoming_subscription_event_record_init(&record, fixture->allocator, event->topic_filter); + + aws_mutex_lock(&fixture->lock); + aws_array_list_push_back(&fixture->subscription_events, &record); + aws_mutex_unlock(&fixture->lock); + aws_condition_variable_notify_all(&fixture->signal); +} + +static void s_rr_mqtt5_protocol_adapter_incoming_publish(struct aws_protocol_adapter_incoming_publish_event *publish, void *user_data) { + struct aws_request_response_mqtt5_adapter_test_fixture *fixture = user_data; + +} + +static void s_rr_mqtt5_protocol_adapter_terminate_callback(void *user_data) { + struct aws_request_response_mqtt5_adapter_test_fixture *fixture = user_data; +} + +static void s_rr_mqtt5_protocol_adapter_connection_event(struct aws_protocol_adapter_connection_event *event, void *user_data) { + struct aws_request_response_mqtt5_adapter_test_fixture *fixture = user_data; +} + +static int s_aws_request_response_mqtt5_adapter_test_fixture_init( + struct aws_request_response_mqtt5_adapter_test_fixture *fixture, + struct aws_allocator *allocator, + struct aws_mqtt5_client_mqtt5_mock_test_fixture_options *mqtt5_fixture_config) { + + AWS_ZERO_STRUCT(*fixture); + + fixture->allocator = allocator; + + if (aws_mqtt5_client_mock_test_fixture_init(&fixture->mqtt5_fixture, allocator, mqtt5_fixture_config)) { + return AWS_OP_ERR; + } + + struct aws_mqtt_protocol_adapter_options protocol_adapter_options = { + .subscription_event_callback = s_rr_mqtt5_protocol_adapter_subscription_event, + .incoming_publish_callback = s_rr_mqtt5_protocol_adapter_incoming_publish, + .terminate_callback = s_rr_mqtt5_protocol_adapter_terminate_callback, + .connection_event_callback = s_rr_mqtt5_protocol_adapter_connection_event, + .user_data = fixture + }; + + fixture->protocol_adapter = aws_mqtt_protocol_adapter_new_from_5(allocator, &protocol_adapter_options, fixture->mqtt5_fixture.client); + AWS_FATAL_ASSERT(fixture->protocol_adapter != NULL); + + aws_array_list_init_dynamic(&fixture->incoming_publish_events, allocator, 10, sizeof(struct request_response_protocol_adapter_incoming_publish_event_record)); + aws_array_list_init_dynamic(&fixture->connection_events, allocator, 10, sizeof(struct request_response_protocol_adapter_connection_event_record)); + aws_array_list_init_dynamic(&fixture->subscription_events, allocator, 10, sizeof(struct request_response_protocol_adapter_subscription_event_record)); + + aws_mutex_init(&fixture->lock); + aws_condition_variable_init(&fixture->signal); + + return AWS_OP_SUCCESS; +} + +static void s_aws_mqtt5_to_mqtt3_adapter_test_fixture_clean_up(struct aws_request_response_mqtt5_adapter_test_fixture *fixture) { + + aws_mqtt5_client_mock_test_fixture_clean_up(&fixture->mqtt5_fixture); + + aws_mutex_clean_up(&fixture->lock); + aws_condition_variable_clean_up(&fixture->signal); +} From b23c567509ba0eae18fce6e9e18009c7b239c999 Mon Sep 17 00:00:00 2001 From: Bret Ambrose Date: Thu, 18 Jan 2024 08:39:21 -0800 Subject: [PATCH 05/19] Another sync point --- tests/CMakeLists.txt | 2 +- .../request_response_protocol_adapter_tests.c | 68 ++++++++++++++++--- 2 files changed, 61 insertions(+), 9 deletions(-) diff --git a/tests/CMakeLists.txt b/tests/CMakeLists.txt index f1d90e22..9bcbb720 100644 --- a/tests/CMakeLists.txt +++ b/tests/CMakeLists.txt @@ -442,7 +442,7 @@ add_test_case(mqtt_subscription_set_publish_single_level_wildcards) add_test_case(mqtt_subscription_set_publish_multi_level_wildcards) add_test_case(mqtt_subscription_set_get_subscriptions) -add_test_case(request_response_mqtt5_protocol_adapter_subscribe_success) +#add_test_case(request_response_mqtt5_protocol_adapter_subscribe_success) #add_test_case(request_response_mqtt5_protocol_adapter_subscribe_failure_error_code) #add_test_case(request_response_mqtt5_protocol_adapter_subscribe_failure_reason_code) #add_test_case(request_response_mqtt5_protocol_adapter_subscribe_failure_timeout) diff --git a/tests/v5/request_response_protocol_adapter_tests.c b/tests/v5/request_response_protocol_adapter_tests.c index 29abcd99..f635e126 100644 --- a/tests/v5/request_response_protocol_adapter_tests.c +++ b/tests/v5/request_response_protocol_adapter_tests.c @@ -58,12 +58,14 @@ struct aws_request_response_mqtt5_adapter_test_fixture { struct aws_array_list connection_events; struct aws_array_list subscription_events; + bool adapter_terminated; + struct aws_mutex lock; struct aws_condition_variable signal; }; -static void s_rr_mqtt5_protocol_adapter_subscription_event(struct aws_protocol_adapter_subscription_event *event, void *user_data) { +static void s_rr_mqtt5_protocol_adapter_test_on_subscription_event(struct aws_protocol_adapter_subscription_event *event, void *user_data) { struct aws_request_response_mqtt5_adapter_test_fixture *fixture = user_data; struct request_response_protocol_adapter_subscription_event_record record = { @@ -77,17 +79,40 @@ static void s_rr_mqtt5_protocol_adapter_subscription_event(struct aws_protocol_a aws_condition_variable_notify_all(&fixture->signal); } -static void s_rr_mqtt5_protocol_adapter_incoming_publish(struct aws_protocol_adapter_incoming_publish_event *publish, void *user_data) { +static void s_rr_mqtt5_protocol_adapter_test_on_incoming_publish(struct aws_protocol_adapter_incoming_publish_event *publish, void *user_data) { struct aws_request_response_mqtt5_adapter_test_fixture *fixture = user_data; + struct request_response_protocol_adapter_incoming_publish_event_record record; + AWS_ZERO_STRUCT(record); + s_request_response_protocol_adapter_incoming_publish_event_record_init(&record, fixture->allocator, publish->topic, publish->payload); + + aws_mutex_lock(&fixture->lock); + aws_array_list_push_back(&fixture->incoming_publish_events, &record); + aws_mutex_unlock(&fixture->lock); + aws_condition_variable_notify_all(&fixture->signal); } -static void s_rr_mqtt5_protocol_adapter_terminate_callback(void *user_data) { +static void s_rr_mqtt5_protocol_adapter_test_on_terminate_callback(void *user_data) { struct aws_request_response_mqtt5_adapter_test_fixture *fixture = user_data; + + aws_mutex_lock(&fixture->lock); + fixture->adapter_terminated = true; + aws_mutex_unlock(&fixture->lock); + aws_condition_variable_notify_all(&fixture->signal); } -static void s_rr_mqtt5_protocol_adapter_connection_event(struct aws_protocol_adapter_connection_event *event, void *user_data) { +static void s_rr_mqtt5_protocol_adapter_test_on_connection_event(struct aws_protocol_adapter_connection_event *event, void *user_data) { struct aws_request_response_mqtt5_adapter_test_fixture *fixture = user_data; + + struct request_response_protocol_adapter_connection_event_record record = { + .event_type = event->event_type, + .rejoined_session = event->rejoined_session + }; + + aws_mutex_lock(&fixture->lock); + aws_array_list_push_back(&fixture->connection_events, &record); + aws_mutex_unlock(&fixture->lock); + aws_condition_variable_notify_all(&fixture->signal); } static int s_aws_request_response_mqtt5_adapter_test_fixture_init( @@ -104,10 +129,10 @@ static int s_aws_request_response_mqtt5_adapter_test_fixture_init( } struct aws_mqtt_protocol_adapter_options protocol_adapter_options = { - .subscription_event_callback = s_rr_mqtt5_protocol_adapter_subscription_event, - .incoming_publish_callback = s_rr_mqtt5_protocol_adapter_incoming_publish, - .terminate_callback = s_rr_mqtt5_protocol_adapter_terminate_callback, - .connection_event_callback = s_rr_mqtt5_protocol_adapter_connection_event, + .subscription_event_callback = s_rr_mqtt5_protocol_adapter_test_on_subscription_event, + .incoming_publish_callback = s_rr_mqtt5_protocol_adapter_test_on_incoming_publish, + .terminate_callback = s_rr_mqtt5_protocol_adapter_test_on_terminate_callback, + .connection_event_callback = s_rr_mqtt5_protocol_adapter_test_on_connection_event, .user_data = fixture }; @@ -124,10 +149,37 @@ static int s_aws_request_response_mqtt5_adapter_test_fixture_init( return AWS_OP_SUCCESS; } +static bool s_is_adapter_terminated(void *context) { + struct aws_request_response_mqtt5_adapter_test_fixture *fixture = context; + + return fixture->adapter_terminated; +} + static void s_aws_mqtt5_to_mqtt3_adapter_test_fixture_clean_up(struct aws_request_response_mqtt5_adapter_test_fixture *fixture) { + aws_mqtt_protocol_adapter_delete(fixture->protocol_adapter); + + aws_mutex_lock(&fixture->lock); + aws_condition_variable_wait_pred(&fixture->signal, &fixture->signal, s_is_adapter_terminated, fixture); + aws_mutex_unlock(&fixture->lock); aws_mqtt5_client_mock_test_fixture_clean_up(&fixture->mqtt5_fixture); + for (size_t i = 0; i < aws_array_list_length(&fixture->subscription_events); ++i) { + struct request_response_protocol_adapter_subscription_event_record record; + aws_array_list_get_at(&fixture->subscription_events, &record, i); + s_request_response_protocol_adapter_incoming_subscription_event_record_cleanup(&record); + } + aws_array_list_clean_up(&fixture->subscription_events); + + for (size_t i = 0; i < aws_array_list_length(&fixture->incoming_publish_events); ++i) { + struct request_response_protocol_adapter_incoming_publish_event_record record; + aws_array_list_get_at(&fixture->incoming_publish_events, &record, i); + s_request_response_protocol_adapter_incoming_publish_event_record_cleanup(&record); + } + aws_array_list_clean_up(&fixture->incoming_publish_events); + + aws_array_list_clean_up(&fixture->connection_events); + aws_mutex_clean_up(&fixture->lock); aws_condition_variable_clean_up(&fixture->signal); } From 37143366de8d911227640516a0d714f8f9774908 Mon Sep 17 00:00:00 2001 From: Bret Ambrose Date: Thu, 18 Jan 2024 14:21:02 -0800 Subject: [PATCH 06/19] First test checkpoint: --- CMakeLists.txt | 1 + .../request-response/protocol_adapter.h | 1 + source/request-response/protocol_adapter.c | 16 +- source/request-response/weak_ref.c | 2 +- tests/CMakeLists.txt | 2 +- .../request_response_protocol_adapter_tests.c | 372 ++++++++++++++++++ tests/v5/mqtt5_client_tests.c | 8 +- tests/v5/mqtt5_testing_utils.h | 5 + tests/v5/mqtt5_to_mqtt3_adapter_tests.c | 4 - .../request_response_protocol_adapter_tests.c | 185 --------- 10 files changed, 392 insertions(+), 204 deletions(-) create mode 100644 tests/request_response_protocol_adapter_tests.c delete mode 100644 tests/v5/request_response_protocol_adapter_tests.c diff --git a/CMakeLists.txt b/CMakeLists.txt index b33f24d6..0a2eeefa 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -57,6 +57,7 @@ file(GLOB AWS_MQTT_PRIV_EXPOSED_HEADERS file(GLOB AWS_MQTT_SRC "source/*.c" "source/v5/*.c" + "source/request-response/*.c" ) file(GLOB MQTT_HEADERS diff --git a/include/aws/mqtt/private/request-response/protocol_adapter.h b/include/aws/mqtt/private/request-response/protocol_adapter.h index 3cf39a07..86d5a44f 100644 --- a/include/aws/mqtt/private/request-response/protocol_adapter.h +++ b/include/aws/mqtt/private/request-response/protocol_adapter.h @@ -6,6 +6,7 @@ * SPDX-License-Identifier: Apache-2.0. */ +#include #include #include diff --git a/source/request-response/protocol_adapter.c b/source/request-response/protocol_adapter.c index 4aae75cd..d1a649b0 100644 --- a/source/request-response/protocol_adapter.c +++ b/source/request-response/protocol_adapter.c @@ -48,7 +48,7 @@ struct aws_mqtt_protocol_adapter *aws_mqtt_protocol_adapter_new_from_311(struct struct aws_mqtt_protocol_adapter_5_impl { struct aws_allocator *allocator; struct aws_mqtt_protocol_adapter base; - struct aws_protocol_adapter_weak_ref *callback_ref; + struct aws_weak_ref *callback_ref; struct aws_mqtt_protocol_adapter_options config; struct aws_event_loop *loop; @@ -68,10 +68,10 @@ struct aws_mqtt_protocol_adapter_5_subscription_op_data { struct aws_allocator *allocator; struct aws_byte_buf topic_filter; - struct aws_protocol_adapter_weak_ref *callback_ref; + struct aws_weak_ref *callback_ref; }; -static struct aws_mqtt_protocol_adapter_5_subscription_op_data *s_aws_mqtt_protocol_adapter_5_subscription_op_data_new(struct aws_allocator *allocator, struct aws_byte_cursor topic_filter, struct aws_protocol_adapter_weak_ref *callback_ref) { +static struct aws_mqtt_protocol_adapter_5_subscription_op_data *s_aws_mqtt_protocol_adapter_5_subscription_op_data_new(struct aws_allocator *allocator, struct aws_byte_cursor topic_filter, struct aws_weak_ref *callback_ref) { struct aws_mqtt_protocol_adapter_5_subscription_op_data *subscribe_data = aws_mem_calloc(allocator, 1, sizeof(struct aws_mqtt_protocol_adapter_5_subscription_op_data)); subscribe_data->allocator = allocator; @@ -207,13 +207,13 @@ int s_aws_mqtt_protocol_adapter_5_unsubscribe(void *impl, struct aws_protocol_ad struct aws_mqtt_protocol_adapter_5_publish_op_data { struct aws_allocator *allocator; - struct aws_protocol_adapter_weak_ref *callback_ref; + struct aws_weak_ref *callback_ref; void (*completion_callback_fn)(bool, void *); void *user_data; }; -static struct aws_mqtt_protocol_adapter_5_publish_op_data *s_aws_mqtt_protocol_adapter_5_publish_op_data_new(struct aws_allocator *allocator, const struct aws_protocol_adapter_publish_options *publish_options, struct aws_protocol_adapter_weak_ref *callback_ref) { +static struct aws_mqtt_protocol_adapter_5_publish_op_data *s_aws_mqtt_protocol_adapter_5_publish_op_data_new(struct aws_allocator *allocator, const struct aws_protocol_adapter_publish_options *publish_options, struct aws_weak_ref *callback_ref) { struct aws_mqtt_protocol_adapter_5_publish_op_data *publish_data = aws_mem_calloc(allocator, 1, sizeof(struct aws_mqtt_protocol_adapter_5_publish_op_data)); publish_data->allocator = allocator; @@ -347,8 +347,6 @@ static struct aws_mqtt_protocol_adapter_vtable s_protocol_adapter_mqtt5_vtable = }; struct aws_mqtt_protocol_adapter *aws_mqtt_protocol_adapter_new_from_5(struct aws_allocator *allocator, struct aws_mqtt_protocol_adapter_options *options, struct aws_mqtt5_client *client) { - AWS_FATAL_ASSERT(aws_event_loop_thread_is_callers_thread(client->loop)); - struct aws_mqtt_protocol_adapter_5_impl *adapter = aws_mem_calloc(allocator, 1, sizeof(struct aws_mqtt_protocol_adapter_5_impl)); adapter->allocator = allocator; @@ -373,7 +371,7 @@ struct aws_mqtt_protocol_adapter *aws_mqtt_protocol_adapter_new_from_5(struct aw adapter->listener = aws_mqtt5_listener_new(allocator, &listener_options); - return adapter; + return &adapter->base; } void aws_mqtt_protocol_adapter_delete(struct aws_mqtt_protocol_adapter *adapter) { @@ -390,4 +388,4 @@ int aws_mqtt_protocol_adapter_unsubscribe(struct aws_mqtt_protocol_adapter *adap int aws_mqtt_protocol_adapter_publish(struct aws_mqtt_protocol_adapter *adapter, struct aws_protocol_adapter_publish_options *options) { return (*adapter->vtable->aws_mqtt_protocol_adapter_publish_fn)(adapter->impl, options); -} \ No newline at end of file +} diff --git a/source/request-response/weak_ref.c b/source/request-response/weak_ref.c index e3e5009c..97561ea9 100644 --- a/source/request-response/weak_ref.c +++ b/source/request-response/weak_ref.c @@ -51,4 +51,4 @@ void *aws_weak_ref_get_reference(struct aws_weak_ref *weak_ref) { void aws_weak_ref_zero_reference(struct aws_weak_ref *weak_ref) { weak_ref->referenced = NULL; -} \ No newline at end of file +} diff --git a/tests/CMakeLists.txt b/tests/CMakeLists.txt index 9bcbb720..f1d90e22 100644 --- a/tests/CMakeLists.txt +++ b/tests/CMakeLists.txt @@ -442,7 +442,7 @@ add_test_case(mqtt_subscription_set_publish_single_level_wildcards) add_test_case(mqtt_subscription_set_publish_multi_level_wildcards) add_test_case(mqtt_subscription_set_get_subscriptions) -#add_test_case(request_response_mqtt5_protocol_adapter_subscribe_success) +add_test_case(request_response_mqtt5_protocol_adapter_subscribe_success) #add_test_case(request_response_mqtt5_protocol_adapter_subscribe_failure_error_code) #add_test_case(request_response_mqtt5_protocol_adapter_subscribe_failure_reason_code) #add_test_case(request_response_mqtt5_protocol_adapter_subscribe_failure_timeout) diff --git a/tests/request_response_protocol_adapter_tests.c b/tests/request_response_protocol_adapter_tests.c new file mode 100644 index 00000000..da53747b --- /dev/null +++ b/tests/request_response_protocol_adapter_tests.c @@ -0,0 +1,372 @@ +/** +* Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. +* SPDX-License-Identifier: Apache-2.0. +*/ + +#include "v5/mqtt5_testing_utils.h" +#include + +#include "aws/mqtt/private/request-response/protocol_adapter.h" + +#include + +struct request_response_protocol_adapter_incoming_publish_event_record { + struct aws_byte_buf topic; + struct aws_byte_buf payload; +}; + +static void s_request_response_protocol_adapter_incoming_publish_event_record_init( + struct request_response_protocol_adapter_incoming_publish_event_record *record, + struct aws_allocator *allocator, + struct aws_byte_cursor topic, + struct aws_byte_cursor payload) { + + aws_byte_buf_init_copy_from_cursor(&record->topic, allocator, topic); + aws_byte_buf_init_copy_from_cursor(&record->payload, allocator, payload); +} + +static void s_request_response_protocol_adapter_incoming_publish_event_record_cleanup(struct request_response_protocol_adapter_incoming_publish_event_record *record) { + aws_byte_buf_clean_up(&record->topic); + aws_byte_buf_clean_up(&record->payload); +} + +struct request_response_protocol_adapter_connection_event_record { + enum aws_protocol_adapter_connection_event_type event_type; + bool rejoined_session; +}; + +struct request_response_protocol_adapter_subscription_event_record { + enum aws_protocol_adapter_subscription_event_type event_type; + struct aws_byte_buf topic_filter; +}; + +static void s_request_response_protocol_adapter_subscription_event_record_init( + struct request_response_protocol_adapter_subscription_event_record *record, + struct aws_allocator *allocator, + struct aws_byte_cursor topic_filter) { + + aws_byte_buf_init_copy_from_cursor(&record->topic_filter, allocator, topic_filter); +} + +static void s_request_response_protocol_adapter_subscription_event_record_cleanup(struct request_response_protocol_adapter_subscription_event_record *record) { + aws_byte_buf_clean_up(&record->topic_filter); +} + +struct aws_request_response_mqtt5_adapter_test_fixture { + struct aws_allocator *allocator; + struct aws_mqtt5_client_mock_test_fixture mqtt5_fixture; + + struct aws_mqtt_protocol_adapter *protocol_adapter; + + struct aws_array_list incoming_publish_events; + struct aws_array_list connection_events; + struct aws_array_list subscription_events; + + bool adapter_terminated; + + struct aws_mutex lock; + struct aws_condition_variable signal; +}; + + +static void s_rr_mqtt5_protocol_adapter_test_on_subscription_event(struct aws_protocol_adapter_subscription_event *event, void *user_data) { + struct aws_request_response_mqtt5_adapter_test_fixture *fixture = user_data; + + struct request_response_protocol_adapter_subscription_event_record record = { + .event_type = event->event_type + }; + s_request_response_protocol_adapter_subscription_event_record_init(&record, fixture->allocator, event->topic_filter); + + aws_mutex_lock(&fixture->lock); + aws_array_list_push_back(&fixture->subscription_events, &record); + aws_mutex_unlock(&fixture->lock); + aws_condition_variable_notify_all(&fixture->signal); +} + +static void s_rr_mqtt5_protocol_adapter_test_on_incoming_publish(struct aws_protocol_adapter_incoming_publish_event *publish, void *user_data) { + struct aws_request_response_mqtt5_adapter_test_fixture *fixture = user_data; + + struct request_response_protocol_adapter_incoming_publish_event_record record; + AWS_ZERO_STRUCT(record); + s_request_response_protocol_adapter_incoming_publish_event_record_init(&record, fixture->allocator, publish->topic, publish->payload); + + aws_mutex_lock(&fixture->lock); + aws_array_list_push_back(&fixture->incoming_publish_events, &record); + aws_mutex_unlock(&fixture->lock); + aws_condition_variable_notify_all(&fixture->signal); +} + +static void s_rr_mqtt5_protocol_adapter_test_on_terminate_callback(void *user_data) { + struct aws_request_response_mqtt5_adapter_test_fixture *fixture = user_data; + + aws_mutex_lock(&fixture->lock); + fixture->adapter_terminated = true; + aws_mutex_unlock(&fixture->lock); + aws_condition_variable_notify_all(&fixture->signal); +} + +static void s_rr_mqtt5_protocol_adapter_test_on_connection_event(struct aws_protocol_adapter_connection_event *event, void *user_data) { + struct aws_request_response_mqtt5_adapter_test_fixture *fixture = user_data; + + struct request_response_protocol_adapter_connection_event_record record = { + .event_type = event->event_type, + .rejoined_session = event->rejoined_session + }; + + aws_mutex_lock(&fixture->lock); + aws_array_list_push_back(&fixture->connection_events, &record); + aws_mutex_unlock(&fixture->lock); + aws_condition_variable_notify_all(&fixture->signal); +} + +static int s_aws_request_response_mqtt5_adapter_test_fixture_init( + struct aws_request_response_mqtt5_adapter_test_fixture *fixture, + struct aws_allocator *allocator, + struct aws_mqtt5_client_mqtt5_mock_test_fixture_options *mqtt5_fixture_config) { + + AWS_ZERO_STRUCT(*fixture); + + fixture->allocator = allocator; + + if (aws_mqtt5_client_mock_test_fixture_init(&fixture->mqtt5_fixture, allocator, mqtt5_fixture_config)) { + return AWS_OP_ERR; + } + + struct aws_mqtt_protocol_adapter_options protocol_adapter_options = { + .subscription_event_callback = s_rr_mqtt5_protocol_adapter_test_on_subscription_event, + .incoming_publish_callback = s_rr_mqtt5_protocol_adapter_test_on_incoming_publish, + .terminate_callback = s_rr_mqtt5_protocol_adapter_test_on_terminate_callback, + .connection_event_callback = s_rr_mqtt5_protocol_adapter_test_on_connection_event, + .user_data = fixture + }; + + fixture->protocol_adapter = aws_mqtt_protocol_adapter_new_from_5(allocator, &protocol_adapter_options, fixture->mqtt5_fixture.client); + AWS_FATAL_ASSERT(fixture->protocol_adapter != NULL); + + aws_array_list_init_dynamic(&fixture->incoming_publish_events, allocator, 10, sizeof(struct request_response_protocol_adapter_incoming_publish_event_record)); + aws_array_list_init_dynamic(&fixture->connection_events, allocator, 10, sizeof(struct request_response_protocol_adapter_connection_event_record)); + aws_array_list_init_dynamic(&fixture->subscription_events, allocator, 10, sizeof(struct request_response_protocol_adapter_subscription_event_record)); + + aws_mutex_init(&fixture->lock); + aws_condition_variable_init(&fixture->signal); + + return AWS_OP_SUCCESS; +} + +static bool s_is_adapter_terminated(void *context) { + struct aws_request_response_mqtt5_adapter_test_fixture *fixture = context; + + return fixture->adapter_terminated; +} + +static void s_aws_request_response_mqtt5_adapter_test_fixture_clean_up(struct aws_request_response_mqtt5_adapter_test_fixture *fixture) { + aws_mqtt_protocol_adapter_delete(fixture->protocol_adapter); + + aws_mutex_lock(&fixture->lock); + aws_condition_variable_wait_pred(&fixture->signal, &fixture->lock, s_is_adapter_terminated, fixture); + aws_mutex_unlock(&fixture->lock); + + aws_mqtt5_client_mock_test_fixture_clean_up(&fixture->mqtt5_fixture); + + for (size_t i = 0; i < aws_array_list_length(&fixture->subscription_events); ++i) { + struct request_response_protocol_adapter_subscription_event_record record; + aws_array_list_get_at(&fixture->subscription_events, &record, i); + s_request_response_protocol_adapter_subscription_event_record_cleanup(&record); + } + aws_array_list_clean_up(&fixture->subscription_events); + + for (size_t i = 0; i < aws_array_list_length(&fixture->incoming_publish_events); ++i) { + struct request_response_protocol_adapter_incoming_publish_event_record record; + aws_array_list_get_at(&fixture->incoming_publish_events, &record, i); + s_request_response_protocol_adapter_incoming_publish_event_record_cleanup(&record); + } + aws_array_list_clean_up(&fixture->incoming_publish_events); + + aws_array_list_clean_up(&fixture->connection_events); + + aws_mutex_clean_up(&fixture->lock); + aws_condition_variable_clean_up(&fixture->signal); +} + +struct test_subscription_event_wait_context { + struct request_response_protocol_adapter_subscription_event_record *expected_event; + size_t expected_count; + struct aws_request_response_mqtt5_adapter_test_fixture *fixture; +}; + +static bool s_do_subscription_events_contain(void *context) { + struct test_subscription_event_wait_context *wait_context = context; + + size_t found = 0; + + size_t num_events = aws_array_list_length(&wait_context->fixture->subscription_events); + for (size_t i = 0; i < num_events; ++i) { + struct request_response_protocol_adapter_subscription_event_record record; + aws_array_list_get_at(&wait_context->fixture->subscription_events, &record, i); + + if (record.event_type == wait_context->expected_event->event_type) { + struct aws_byte_cursor record_topic_filter = aws_byte_cursor_from_buf(&record.topic_filter); + struct aws_byte_cursor expected_topic_filter = aws_byte_cursor_from_buf(&wait_context->expected_event->topic_filter); + if (aws_byte_cursor_eq(&record_topic_filter, &expected_topic_filter)) { + ++found; + } + } + } + + return found >= wait_context->expected_count; +} + +static void s_wait_for_subscription_events_contains(struct aws_request_response_mqtt5_adapter_test_fixture *fixture, + struct request_response_protocol_adapter_subscription_event_record *expected_event, + size_t expected_count) { + + struct test_subscription_event_wait_context context = { + .expected_event = expected_event, + .expected_count = expected_count, + .fixture = fixture, + }; + + aws_mutex_lock(&fixture->lock); + aws_condition_variable_wait_pred(&fixture->signal, &fixture->lock, s_do_subscription_events_contain, &context); + aws_mutex_unlock(&fixture->lock); +} + +struct test_connection_event_wait_context { + struct request_response_protocol_adapter_connection_event_record *expected_event; + size_t expected_count; + struct aws_request_response_mqtt5_adapter_test_fixture *fixture; +}; + +static bool s_do_connection_events_contain(void *context) { + struct test_connection_event_wait_context *wait_context = context; + + size_t found = 0; + + size_t num_events = aws_array_list_length(&wait_context->fixture->connection_events); + for (size_t i = 0; i < num_events; ++i) { + struct request_response_protocol_adapter_connection_event_record record; + aws_array_list_get_at(&wait_context->fixture->connection_events, &record, i); + + if (record.event_type == wait_context->expected_event->event_type && record.rejoined_session == wait_context->expected_event->rejoined_session) { + ++found; + } + } + + return found >= wait_context->expected_count; +} + +static void s_wait_for_connection_events_contains(struct aws_request_response_mqtt5_adapter_test_fixture *fixture, + struct request_response_protocol_adapter_connection_event_record *expected_event, + size_t expected_count) { + + struct test_connection_event_wait_context context = { + .expected_event = expected_event, + .expected_count = expected_count, + .fixture = fixture, + }; + + aws_mutex_lock(&fixture->lock); + aws_condition_variable_wait_pred(&fixture->signal, &fixture->lock, s_do_connection_events_contain, &context); + aws_mutex_unlock(&fixture->lock); +} + +struct test_incoming_publish_event_wait_context { + struct request_response_protocol_adapter_incoming_publish_event_record *expected_event; + size_t expected_count; + struct aws_request_response_mqtt5_adapter_test_fixture *fixture; +}; + +static bool s_do_incoming_publish_events_contain(void *context) { + struct test_incoming_publish_event_wait_context *wait_context = context; + + size_t found = 0; + + size_t num_events = aws_array_list_length(&wait_context->fixture->incoming_publish_events); + for (size_t i = 0; i < num_events; ++i) { + struct request_response_protocol_adapter_incoming_publish_event_record record; + aws_array_list_get_at(&wait_context->fixture->incoming_publish_events, &record, i); + + struct aws_byte_cursor record_topic = aws_byte_cursor_from_buf(&record.topic); + struct aws_byte_cursor expected_topic = aws_byte_cursor_from_buf(&wait_context->expected_event->topic); + if (!aws_byte_cursor_eq(&record_topic, &expected_topic)) { + continue; + } + + struct aws_byte_cursor record_payload = aws_byte_cursor_from_buf(&record.payload); + struct aws_byte_cursor expected_payload = aws_byte_cursor_from_buf(&wait_context->expected_event->payload); + if (!aws_byte_cursor_eq(&record_payload, &expected_payload)) { + continue; + } + + ++found; + } + + return found >= wait_context->expected_count; +} + +static void s_wait_for_incoming_publish_events_contains(struct aws_request_response_mqtt5_adapter_test_fixture *fixture, + struct request_response_protocol_adapter_incoming_publish_event_record *expected_event, + size_t expected_count) { + + struct test_incoming_publish_event_wait_context context = { + .expected_event = expected_event, + .expected_count = expected_count, + .fixture = fixture, + }; + + aws_mutex_lock(&fixture->lock); + aws_condition_variable_wait_pred(&fixture->signal, &fixture->lock, s_do_incoming_publish_events_contain, &context); + aws_mutex_unlock(&fixture->lock); +} + +static int s_request_response_mqtt5_protocol_adapter_subscribe_success_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); + + test_options.server_function_table.packet_handlers[AWS_MQTT5_PT_SUBSCRIBE] = + aws_mqtt5_server_send_suback_on_subscribe; + + struct aws_mqtt5_client_mqtt5_mock_test_fixture_options mqtt5_test_fixture_options = { + .client_options = &test_options.client_options, + .server_function_table = &test_options.server_function_table, + }; + + struct aws_request_response_mqtt5_adapter_test_fixture fixture; + ASSERT_SUCCESS(s_aws_request_response_mqtt5_adapter_test_fixture_init(&fixture, allocator, &mqtt5_test_fixture_options)); + + struct aws_mqtt5_client *client = fixture.mqtt5_fixture.client; + ASSERT_SUCCESS(aws_mqtt5_client_start(client)); + + aws_wait_for_connected_lifecycle_event(&fixture.mqtt5_fixture); + + struct request_response_protocol_adapter_subscription_event_record expected_outcome = { + .event_type = AWS_PASET_SUBSCRIBE_SUCCESS, + }; + + aws_byte_buf_init_copy_from_cursor(&expected_outcome.topic_filter, allocator, aws_byte_cursor_from_c_str("hello/world")); + + struct aws_protocol_adapter_subscribe_options subscribe_options = { + .topic_filter = aws_byte_cursor_from_buf(&expected_outcome.topic_filter), + .ack_timeout_seconds = 5, + }; + + aws_mqtt_protocol_adapter_subscribe(fixture.protocol_adapter, &subscribe_options); + + s_wait_for_subscription_events_contains(&fixture, &expected_outcome, 1); + + s_request_response_protocol_adapter_subscription_event_record_cleanup(&expected_outcome); + + s_aws_request_response_mqtt5_adapter_test_fixture_clean_up(&fixture); + + aws_mqtt_library_clean_up(); + + return AWS_OP_SUCCESS; +} + +AWS_TEST_CASE( + request_response_mqtt5_protocol_adapter_subscribe_success, + s_request_response_mqtt5_protocol_adapter_subscribe_success_fn) \ No newline at end of file diff --git a/tests/v5/mqtt5_client_tests.c b/tests/v5/mqtt5_client_tests.c index 06367d3d..7049dbea 100644 --- a/tests/v5/mqtt5_client_tests.c +++ b/tests/v5/mqtt5_client_tests.c @@ -1864,7 +1864,7 @@ static void s_wait_for_suback_received(struct aws_mqtt5_client_mock_test_fixture aws_mutex_unlock(&test_context->lock); } -static int s_aws_mqtt5_server_send_suback_on_subscribe( +int aws_mqtt5_server_send_suback_on_subscribe( void *packet, struct aws_mqtt5_server_mock_connection_context *connection, void *user_data) { @@ -1892,7 +1892,7 @@ static int s_mqtt5_client_subscribe_success_fn(struct aws_allocator *allocator, aws_mqtt5_client_test_init_default_options(&test_options); test_options.server_function_table.packet_handlers[AWS_MQTT5_PT_SUBSCRIBE] = - s_aws_mqtt5_server_send_suback_on_subscribe; + aws_mqtt5_server_send_suback_on_subscribe; struct aws_mqtt5_client_mock_test_fixture test_context; @@ -3241,7 +3241,7 @@ static int s_do_sub_pub_unsub_test(struct aws_allocator *allocator, enum aws_mqt test_options.client_options.publish_received_handler_user_data = &full_test_context; test_options.server_function_table.packet_handlers[AWS_MQTT5_PT_SUBSCRIBE] = - s_aws_mqtt5_server_send_suback_on_subscribe; + aws_mqtt5_server_send_suback_on_subscribe; test_options.server_function_table.packet_handlers[AWS_MQTT5_PT_PUBLISH] = aws_mqtt5_mock_server_handle_publish_puback_and_forward; test_options.server_function_table.packet_handlers[AWS_MQTT5_PT_UNSUBSCRIBE] = @@ -4139,7 +4139,7 @@ static int s_mqtt5_client_statistics_subscribe_fn(struct aws_allocator *allocato aws_mqtt5_client_test_init_default_options(&test_options); test_options.server_function_table.packet_handlers[AWS_MQTT5_PT_SUBSCRIBE] = - s_aws_mqtt5_server_send_suback_on_subscribe; + aws_mqtt5_server_send_suback_on_subscribe; struct aws_mqtt5_client_mock_test_fixture test_context; diff --git a/tests/v5/mqtt5_testing_utils.h b/tests/v5/mqtt5_testing_utils.h index 0e30f246..74cd0f8d 100644 --- a/tests/v5/mqtt5_testing_utils.h +++ b/tests/v5/mqtt5_testing_utils.h @@ -223,6 +223,11 @@ int aws_mqtt5_mock_server_send_packet( enum aws_mqtt5_packet_type packet_type, void *packet); +int aws_mqtt5_server_send_suback_on_subscribe( + void *packet, + struct aws_mqtt5_server_mock_connection_context *connection, + void *user_data); + extern const struct aws_string *g_default_client_id; #define RECONNECT_TEST_MIN_BACKOFF 500 diff --git a/tests/v5/mqtt5_to_mqtt3_adapter_tests.c b/tests/v5/mqtt5_to_mqtt3_adapter_tests.c index e3de0802..b067267a 100644 --- a/tests/v5/mqtt5_to_mqtt3_adapter_tests.c +++ b/tests/v5/mqtt5_to_mqtt3_adapter_tests.c @@ -9,18 +9,14 @@ #include #include #include -#include #include #include #include -#include #include -#include #include #include -#include enum aws_mqtt3_lifecycle_event_type { AWS_MQTT3_LET_CONNECTION_COMPLETE, diff --git a/tests/v5/request_response_protocol_adapter_tests.c b/tests/v5/request_response_protocol_adapter_tests.c deleted file mode 100644 index f635e126..00000000 --- a/tests/v5/request_response_protocol_adapter_tests.c +++ /dev/null @@ -1,185 +0,0 @@ -/** -* Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. -* SPDX-License-Identifier: Apache-2.0. -*/ - -#include "mqtt5_testing_utils.h" -#include - -struct request_response_protocol_adapter_incoming_publish_event_record { - struct aws_byte_buf topic; - struct aws_byte_buf payload; -}; - -static void s_request_response_protocol_adapter_incoming_publish_event_record_init( - struct request_response_protocol_adapter_incoming_publish_event_record *record, - struct aws_allocator *allocator, - struct aws_byte_cursor topic, - struct aws_byte_cursor payload) { - - aws_byte_buf_init_copy_from_cursor(&record->topic, allocator, topic); - aws_byte_buf_init_copy_from_cursor(&record->payload, allocator, payload); -} - -static void s_request_response_protocol_adapter_incoming_publish_event_record_cleanup(struct request_response_protocol_adapter_incoming_publish_event_record *record) { - aws_byte_buf_clean_up(&record->topic); - aws_byte_buf_clean_up(&record->payload); -} - -struct request_response_protocol_adapter_connection_event_record { - enum aws_protocol_adapter_connection_event_type event_type; - bool rejoined_session; -}; - -struct request_response_protocol_adapter_subscription_event_record { - enum aws_protocol_adapter_subscription_event_type event_type; - struct aws_byte_buf topic_filter; -}; - -static void s_request_response_protocol_adapter_incoming_subscription_event_record_init( - struct request_response_protocol_adapter_subscription_event_record *record, - struct aws_allocator *allocator, - struct aws_byte_cursor topic_filter) { - - aws_byte_buf_init_copy_from_cursor(&record->topic_filter, allocator, topic_filter); -} - -static void s_request_response_protocol_adapter_incoming_subscription_event_record_cleanup(struct request_response_protocol_adapter_subscription_event_record *record) { - aws_byte_buf_clean_up(&record->topic_filter); -} - -struct aws_request_response_mqtt5_adapter_test_fixture { - struct aws_allocator *allocator; - struct aws_mqtt5_client_mock_test_fixture mqtt5_fixture; - - struct aws_mqtt_protocol_adapter *protocol_adapter; - - struct aws_array_list incoming_publish_events; - struct aws_array_list connection_events; - struct aws_array_list subscription_events; - - bool adapter_terminated; - - struct aws_mutex lock; - struct aws_condition_variable signal; -}; - - -static void s_rr_mqtt5_protocol_adapter_test_on_subscription_event(struct aws_protocol_adapter_subscription_event *event, void *user_data) { - struct aws_request_response_mqtt5_adapter_test_fixture *fixture = user_data; - - struct request_response_protocol_adapter_subscription_event_record record = { - .event_type = event->event_type - }; - s_request_response_protocol_adapter_incoming_subscription_event_record_init(&record, fixture->allocator, event->topic_filter); - - aws_mutex_lock(&fixture->lock); - aws_array_list_push_back(&fixture->subscription_events, &record); - aws_mutex_unlock(&fixture->lock); - aws_condition_variable_notify_all(&fixture->signal); -} - -static void s_rr_mqtt5_protocol_adapter_test_on_incoming_publish(struct aws_protocol_adapter_incoming_publish_event *publish, void *user_data) { - struct aws_request_response_mqtt5_adapter_test_fixture *fixture = user_data; - - struct request_response_protocol_adapter_incoming_publish_event_record record; - AWS_ZERO_STRUCT(record); - s_request_response_protocol_adapter_incoming_publish_event_record_init(&record, fixture->allocator, publish->topic, publish->payload); - - aws_mutex_lock(&fixture->lock); - aws_array_list_push_back(&fixture->incoming_publish_events, &record); - aws_mutex_unlock(&fixture->lock); - aws_condition_variable_notify_all(&fixture->signal); -} - -static void s_rr_mqtt5_protocol_adapter_test_on_terminate_callback(void *user_data) { - struct aws_request_response_mqtt5_adapter_test_fixture *fixture = user_data; - - aws_mutex_lock(&fixture->lock); - fixture->adapter_terminated = true; - aws_mutex_unlock(&fixture->lock); - aws_condition_variable_notify_all(&fixture->signal); -} - -static void s_rr_mqtt5_protocol_adapter_test_on_connection_event(struct aws_protocol_adapter_connection_event *event, void *user_data) { - struct aws_request_response_mqtt5_adapter_test_fixture *fixture = user_data; - - struct request_response_protocol_adapter_connection_event_record record = { - .event_type = event->event_type, - .rejoined_session = event->rejoined_session - }; - - aws_mutex_lock(&fixture->lock); - aws_array_list_push_back(&fixture->connection_events, &record); - aws_mutex_unlock(&fixture->lock); - aws_condition_variable_notify_all(&fixture->signal); -} - -static int s_aws_request_response_mqtt5_adapter_test_fixture_init( - struct aws_request_response_mqtt5_adapter_test_fixture *fixture, - struct aws_allocator *allocator, - struct aws_mqtt5_client_mqtt5_mock_test_fixture_options *mqtt5_fixture_config) { - - AWS_ZERO_STRUCT(*fixture); - - fixture->allocator = allocator; - - if (aws_mqtt5_client_mock_test_fixture_init(&fixture->mqtt5_fixture, allocator, mqtt5_fixture_config)) { - return AWS_OP_ERR; - } - - struct aws_mqtt_protocol_adapter_options protocol_adapter_options = { - .subscription_event_callback = s_rr_mqtt5_protocol_adapter_test_on_subscription_event, - .incoming_publish_callback = s_rr_mqtt5_protocol_adapter_test_on_incoming_publish, - .terminate_callback = s_rr_mqtt5_protocol_adapter_test_on_terminate_callback, - .connection_event_callback = s_rr_mqtt5_protocol_adapter_test_on_connection_event, - .user_data = fixture - }; - - fixture->protocol_adapter = aws_mqtt_protocol_adapter_new_from_5(allocator, &protocol_adapter_options, fixture->mqtt5_fixture.client); - AWS_FATAL_ASSERT(fixture->protocol_adapter != NULL); - - aws_array_list_init_dynamic(&fixture->incoming_publish_events, allocator, 10, sizeof(struct request_response_protocol_adapter_incoming_publish_event_record)); - aws_array_list_init_dynamic(&fixture->connection_events, allocator, 10, sizeof(struct request_response_protocol_adapter_connection_event_record)); - aws_array_list_init_dynamic(&fixture->subscription_events, allocator, 10, sizeof(struct request_response_protocol_adapter_subscription_event_record)); - - aws_mutex_init(&fixture->lock); - aws_condition_variable_init(&fixture->signal); - - return AWS_OP_SUCCESS; -} - -static bool s_is_adapter_terminated(void *context) { - struct aws_request_response_mqtt5_adapter_test_fixture *fixture = context; - - return fixture->adapter_terminated; -} - -static void s_aws_mqtt5_to_mqtt3_adapter_test_fixture_clean_up(struct aws_request_response_mqtt5_adapter_test_fixture *fixture) { - aws_mqtt_protocol_adapter_delete(fixture->protocol_adapter); - - aws_mutex_lock(&fixture->lock); - aws_condition_variable_wait_pred(&fixture->signal, &fixture->signal, s_is_adapter_terminated, fixture); - aws_mutex_unlock(&fixture->lock); - - aws_mqtt5_client_mock_test_fixture_clean_up(&fixture->mqtt5_fixture); - - for (size_t i = 0; i < aws_array_list_length(&fixture->subscription_events); ++i) { - struct request_response_protocol_adapter_subscription_event_record record; - aws_array_list_get_at(&fixture->subscription_events, &record, i); - s_request_response_protocol_adapter_incoming_subscription_event_record_cleanup(&record); - } - aws_array_list_clean_up(&fixture->subscription_events); - - for (size_t i = 0; i < aws_array_list_length(&fixture->incoming_publish_events); ++i) { - struct request_response_protocol_adapter_incoming_publish_event_record record; - aws_array_list_get_at(&fixture->incoming_publish_events, &record, i); - s_request_response_protocol_adapter_incoming_publish_event_record_cleanup(&record); - } - aws_array_list_clean_up(&fixture->incoming_publish_events); - - aws_array_list_clean_up(&fixture->connection_events); - - aws_mutex_clean_up(&fixture->lock); - aws_condition_variable_clean_up(&fixture->signal); -} From 384a380a2b374851d415f140ff87cb224db41392 Mon Sep 17 00:00:00 2001 From: Bret Ambrose Date: Thu, 18 Jan 2024 15:04:30 -0800 Subject: [PATCH 07/19] Initial subscribe tests --- tests/CMakeLists.txt | 6 +- .../request_response_protocol_adapter_tests.c | 101 ++++++++++++++++-- 2 files changed, 95 insertions(+), 12 deletions(-) diff --git a/tests/CMakeLists.txt b/tests/CMakeLists.txt index f1d90e22..500cf8fb 100644 --- a/tests/CMakeLists.txt +++ b/tests/CMakeLists.txt @@ -443,9 +443,9 @@ add_test_case(mqtt_subscription_set_publish_multi_level_wildcards) add_test_case(mqtt_subscription_set_get_subscriptions) add_test_case(request_response_mqtt5_protocol_adapter_subscribe_success) -#add_test_case(request_response_mqtt5_protocol_adapter_subscribe_failure_error_code) -#add_test_case(request_response_mqtt5_protocol_adapter_subscribe_failure_reason_code) -#add_test_case(request_response_mqtt5_protocol_adapter_subscribe_failure_timeout) +add_test_case(request_response_mqtt5_protocol_adapter_subscribe_failure_error_code) +add_test_case(request_response_mqtt5_protocol_adapter_subscribe_failure_reason_code) +add_test_case(request_response_mqtt5_protocol_adapter_subscribe_failure_timeout) generate_test_driver(${PROJECT_NAME}-tests) diff --git a/tests/request_response_protocol_adapter_tests.c b/tests/request_response_protocol_adapter_tests.c index da53747b..a5d3d1e1 100644 --- a/tests/request_response_protocol_adapter_tests.c +++ b/tests/request_response_protocol_adapter_tests.c @@ -319,16 +319,52 @@ static void s_wait_for_incoming_publish_events_contains(struct aws_request_respo aws_mutex_unlock(&fixture->lock); } -static int s_request_response_mqtt5_protocol_adapter_subscribe_success_fn(struct aws_allocator *allocator, void *ctx) { - (void)ctx; +enum protocol_adapter_operation_test_type { + PAOTT_SUCCESS, + PAOTT_FAILURE_TIMEOUT, + PAOTT_FAILURE_REASON_CODE, + PAOTT_FAILURE_ERROR_CODE, +}; + +static enum aws_mqtt5_suback_reason_code s_failed_suback_reason_codes[] = { + AWS_MQTT5_SARC_NOT_AUTHORIZED, +}; + +static int s_aws_mqtt5_server_send_failed_suback_on_subscribe( + void *packet, + struct aws_mqtt5_server_mock_connection_context *connection, + void *user_data) { + (void)packet; + (void)user_data; + struct aws_mqtt5_packet_subscribe_view *subscribe_view = packet; + + struct aws_mqtt5_packet_suback_view suback_view = { + .packet_id = subscribe_view->packet_id, + .reason_code_count = AWS_ARRAY_SIZE(s_failed_suback_reason_codes), + .reason_codes = s_failed_suback_reason_codes, + }; + + return aws_mqtt5_mock_server_send_packet(connection, AWS_MQTT5_PT_SUBACK, &suback_view); +} + +static int s_do_request_response_mqtt5_protocol_adapter_subscribe_test(struct aws_allocator *allocator, enum protocol_adapter_operation_test_type test_type) { aws_mqtt_library_init(allocator); struct mqtt5_client_test_options test_options; aws_mqtt5_client_test_init_default_options(&test_options); - test_options.server_function_table.packet_handlers[AWS_MQTT5_PT_SUBSCRIBE] = - aws_mqtt5_server_send_suback_on_subscribe; + if (test_type == PAOTT_SUCCESS) { + test_options.server_function_table.packet_handlers[AWS_MQTT5_PT_SUBSCRIBE] = + aws_mqtt5_server_send_suback_on_subscribe; + } else if (test_type == PAOTT_FAILURE_REASON_CODE) { + test_options.server_function_table.packet_handlers[AWS_MQTT5_PT_SUBSCRIBE] = + s_aws_mqtt5_server_send_failed_suback_on_subscribe; + } + + if (test_type == PAOTT_FAILURE_ERROR_CODE) { + test_options.client_options.offline_queue_behavior = AWS_MQTT5_COQBT_FAIL_ALL_ON_DISCONNECT; + } struct aws_mqtt5_client_mqtt5_mock_test_fixture_options mqtt5_test_fixture_options = { .client_options = &test_options.client_options, @@ -339,19 +375,22 @@ static int s_request_response_mqtt5_protocol_adapter_subscribe_success_fn(struct ASSERT_SUCCESS(s_aws_request_response_mqtt5_adapter_test_fixture_init(&fixture, allocator, &mqtt5_test_fixture_options)); struct aws_mqtt5_client *client = fixture.mqtt5_fixture.client; - ASSERT_SUCCESS(aws_mqtt5_client_start(client)); - aws_wait_for_connected_lifecycle_event(&fixture.mqtt5_fixture); + if (test_type != PAOTT_FAILURE_ERROR_CODE) { + ASSERT_SUCCESS(aws_mqtt5_client_start(client)); + + aws_wait_for_connected_lifecycle_event(&fixture.mqtt5_fixture); + } struct request_response_protocol_adapter_subscription_event_record expected_outcome = { - .event_type = AWS_PASET_SUBSCRIBE_SUCCESS, + .event_type = (test_type == PAOTT_SUCCESS) ? AWS_PASET_SUBSCRIBE_SUCCESS : AWS_PASET_SUBSCRIBE_FAILURE, }; aws_byte_buf_init_copy_from_cursor(&expected_outcome.topic_filter, allocator, aws_byte_cursor_from_c_str("hello/world")); struct aws_protocol_adapter_subscribe_options subscribe_options = { .topic_filter = aws_byte_cursor_from_buf(&expected_outcome.topic_filter), - .ack_timeout_seconds = 5, + .ack_timeout_seconds = 2, }; aws_mqtt_protocol_adapter_subscribe(fixture.protocol_adapter, &subscribe_options); @@ -367,6 +406,50 @@ static int s_request_response_mqtt5_protocol_adapter_subscribe_success_fn(struct return AWS_OP_SUCCESS; } +static int s_request_response_mqtt5_protocol_adapter_subscribe_success_fn(struct aws_allocator *allocator, void *ctx) { + (void)ctx; + + ASSERT_SUCCESS(s_do_request_response_mqtt5_protocol_adapter_subscribe_test(allocator, PAOTT_SUCCESS)); + + return AWS_OP_SUCCESS; +} + AWS_TEST_CASE( request_response_mqtt5_protocol_adapter_subscribe_success, - s_request_response_mqtt5_protocol_adapter_subscribe_success_fn) \ No newline at end of file + s_request_response_mqtt5_protocol_adapter_subscribe_success_fn) + +static int s_request_response_mqtt5_protocol_adapter_subscribe_failure_timeout_fn(struct aws_allocator *allocator, void *ctx) { + (void)ctx; + + ASSERT_SUCCESS(s_do_request_response_mqtt5_protocol_adapter_subscribe_test(allocator, PAOTT_FAILURE_TIMEOUT)); + + return AWS_OP_SUCCESS; +} + +AWS_TEST_CASE( + request_response_mqtt5_protocol_adapter_subscribe_failure_timeout, + s_request_response_mqtt5_protocol_adapter_subscribe_failure_timeout_fn) + +static int s_request_response_mqtt5_protocol_adapter_subscribe_failure_reason_code_fn(struct aws_allocator *allocator, void *ctx) { + (void)ctx; + + ASSERT_SUCCESS(s_do_request_response_mqtt5_protocol_adapter_subscribe_test(allocator, PAOTT_FAILURE_REASON_CODE)); + + return AWS_OP_SUCCESS; +} + +AWS_TEST_CASE( + request_response_mqtt5_protocol_adapter_subscribe_failure_reason_code, + s_request_response_mqtt5_protocol_adapter_subscribe_failure_reason_code_fn) + +static int s_request_response_mqtt5_protocol_adapter_subscribe_failure_error_code_fn(struct aws_allocator *allocator, void *ctx) { + (void)ctx; + + ASSERT_SUCCESS(s_do_request_response_mqtt5_protocol_adapter_subscribe_test(allocator, PAOTT_FAILURE_ERROR_CODE)); + + return AWS_OP_SUCCESS; +} + +AWS_TEST_CASE( + request_response_mqtt5_protocol_adapter_subscribe_failure_error_code, + s_request_response_mqtt5_protocol_adapter_subscribe_failure_error_code_fn) From 86de21944016253628ec9159a2da88f75fc66652 Mon Sep 17 00:00:00 2001 From: Bret Ambrose Date: Fri, 19 Jan 2024 13:04:40 -0800 Subject: [PATCH 08/19] Tests complete --- .../request-response/protocol_adapter.h | 34 +- .../mqtt/private/request-response/weak_ref.h | 1 - source/request-response/protocol_adapter.c | 109 +-- source/request-response/weak_ref.c | 6 +- tests/CMakeLists.txt | 11 + .../request_response_protocol_adapter_tests.c | 658 ++++++++++++++++-- tests/v5/mqtt5_client_tests.c | 10 +- tests/v5/mqtt5_testing_utils.h | 10 + 8 files changed, 734 insertions(+), 105 deletions(-) diff --git a/include/aws/mqtt/private/request-response/protocol_adapter.h b/include/aws/mqtt/private/request-response/protocol_adapter.h index 86d5a44f..8a61d351 100644 --- a/include/aws/mqtt/private/request-response/protocol_adapter.h +++ b/include/aws/mqtt/private/request-response/protocol_adapter.h @@ -6,8 +6,8 @@ * SPDX-License-Identifier: Apache-2.0. */ -#include #include +#include #include @@ -61,10 +61,14 @@ struct aws_protocol_adapter_connection_event { bool rejoined_session; }; -typedef void(aws_protocol_adapter_subscription_event_fn)(struct aws_protocol_adapter_subscription_event *event, void *user_data); -typedef void(aws_protocol_adapter_incoming_publish_fn)(struct aws_protocol_adapter_incoming_publish_event *publish, void *user_data); +typedef void( + aws_protocol_adapter_subscription_event_fn)(struct aws_protocol_adapter_subscription_event *event, void *user_data); +typedef void(aws_protocol_adapter_incoming_publish_fn)( + struct aws_protocol_adapter_incoming_publish_event *publish, + void *user_data); typedef void(aws_protocol_adapter_terminate_callback_fn)(void *user_data); -typedef void(aws_protocol_adapter_connection_event_fn)(struct aws_protocol_adapter_connection_event *event, void *user_data); +typedef void( + aws_protocol_adapter_connection_event_fn)(struct aws_protocol_adapter_connection_event *event, void *user_data); struct aws_mqtt_protocol_adapter_options { aws_protocol_adapter_subscription_event_fn *subscription_event_callback; @@ -93,17 +97,29 @@ struct aws_mqtt_protocol_adapter { AWS_EXTERN_C_BEGIN -AWS_MQTT_API struct aws_mqtt_protocol_adapter *aws_mqtt_protocol_adapter_new_from_311(struct aws_allocator *allocator, struct aws_mqtt_protocol_adapter_options *options, struct aws_mqtt_client_connection *connection); +AWS_MQTT_API struct aws_mqtt_protocol_adapter *aws_mqtt_protocol_adapter_new_from_311( + struct aws_allocator *allocator, + struct aws_mqtt_protocol_adapter_options *options, + struct aws_mqtt_client_connection *connection); -AWS_MQTT_API struct aws_mqtt_protocol_adapter *aws_mqtt_protocol_adapter_new_from_5(struct aws_allocator *allocator, struct aws_mqtt_protocol_adapter_options *options, struct aws_mqtt5_client *client); +AWS_MQTT_API struct aws_mqtt_protocol_adapter *aws_mqtt_protocol_adapter_new_from_5( + struct aws_allocator *allocator, + struct aws_mqtt_protocol_adapter_options *options, + struct aws_mqtt5_client *client); AWS_MQTT_API void aws_mqtt_protocol_adapter_delete(struct aws_mqtt_protocol_adapter *adapter); -AWS_MQTT_API int aws_mqtt_protocol_adapter_subscribe(struct aws_mqtt_protocol_adapter *adapter, struct aws_protocol_adapter_subscribe_options *options); +AWS_MQTT_API int aws_mqtt_protocol_adapter_subscribe( + struct aws_mqtt_protocol_adapter *adapter, + struct aws_protocol_adapter_subscribe_options *options); -AWS_MQTT_API int aws_mqtt_protocol_adapter_unsubscribe(struct aws_mqtt_protocol_adapter *adapter, struct aws_protocol_adapter_unsubscribe_options *options); +AWS_MQTT_API int aws_mqtt_protocol_adapter_unsubscribe( + struct aws_mqtt_protocol_adapter *adapter, + struct aws_protocol_adapter_unsubscribe_options *options); -AWS_MQTT_API int aws_mqtt_protocol_adapter_publish(struct aws_mqtt_protocol_adapter *adapter, struct aws_protocol_adapter_publish_options *options); +AWS_MQTT_API int aws_mqtt_protocol_adapter_publish( + struct aws_mqtt_protocol_adapter *adapter, + struct aws_protocol_adapter_publish_options *options); AWS_EXTERN_C_END diff --git a/include/aws/mqtt/private/request-response/weak_ref.h b/include/aws/mqtt/private/request-response/weak_ref.h index 34968874..0872de69 100644 --- a/include/aws/mqtt/private/request-response/weak_ref.h +++ b/include/aws/mqtt/private/request-response/weak_ref.h @@ -26,5 +26,4 @@ AWS_MQTT_API void aws_weak_ref_zero_reference(struct aws_weak_ref *weak_ref); AWS_EXTERN_C_END - #endif /* AWS_MQTT_PRIVATE_REQUEST_RESPONSE_WEAK_REF_H */ diff --git a/source/request-response/protocol_adapter.c b/source/request-response/protocol_adapter.c index d1a649b0..a4dc656c 100644 --- a/source/request-response/protocol_adapter.c +++ b/source/request-response/protocol_adapter.c @@ -21,9 +21,9 @@ * * Entries are not tracked with the exception of eventstream impl which needs the stream handles to close. * A subscribe failure should not trigger an unsubscribe, only notify the status callback. - * Subscription event callback should be {subscribe_success, subscribe_failure, unsubscribe_success, unsubscribe_failure}. - * The sub manager is responsible for calling Unsubscribe on all its entries when shutting down (before releasing - * hold of the adapter). + * Subscription event callback should be {subscribe_success, subscribe_failure, unsubscribe_success, + * unsubscribe_failure}. The sub manager is responsible for calling Unsubscribe on all its entries when shutting down + * (before releasing hold of the adapter). * * How do we know not to retry unsubscribe failures because a subscribe came in? Well, we don't retry failures; let * the manager make that decision. No retry when the weak ref is zeroed either. The potential for things to go wrong @@ -33,8 +33,10 @@ * decide what to do. */ - -struct aws_mqtt_protocol_adapter *aws_mqtt_protocol_adapter_new_from_311(struct aws_allocator *allocator, struct aws_mqtt_protocol_adapter_options *options, struct aws_mqtt_client_connection *connection) { +struct aws_mqtt_protocol_adapter *aws_mqtt_protocol_adapter_new_from_311( + struct aws_allocator *allocator, + struct aws_mqtt_protocol_adapter_options *options, + struct aws_mqtt_client_connection *connection) { (void)allocator; (void)options; (void)connection; @@ -71,8 +73,12 @@ struct aws_mqtt_protocol_adapter_5_subscription_op_data { struct aws_weak_ref *callback_ref; }; -static struct aws_mqtt_protocol_adapter_5_subscription_op_data *s_aws_mqtt_protocol_adapter_5_subscription_op_data_new(struct aws_allocator *allocator, struct aws_byte_cursor topic_filter, struct aws_weak_ref *callback_ref) { - struct aws_mqtt_protocol_adapter_5_subscription_op_data *subscribe_data = aws_mem_calloc(allocator, 1, sizeof(struct aws_mqtt_protocol_adapter_5_subscription_op_data)); +static struct aws_mqtt_protocol_adapter_5_subscription_op_data *s_aws_mqtt_protocol_adapter_5_subscription_op_data_new( + struct aws_allocator *allocator, + struct aws_byte_cursor topic_filter, + struct aws_weak_ref *callback_ref) { + struct aws_mqtt_protocol_adapter_5_subscription_op_data *subscribe_data = + aws_mem_calloc(allocator, 1, sizeof(struct aws_mqtt_protocol_adapter_5_subscription_op_data)); subscribe_data->allocator = allocator; subscribe_data->callback_ref = aws_weak_ref_acquire(callback_ref); @@ -81,7 +87,8 @@ static struct aws_mqtt_protocol_adapter_5_subscription_op_data *s_aws_mqtt_proto return subscribe_data; } -static void s_aws_mqtt_protocol_adapter_5_subscription_op_data_delete(struct aws_mqtt_protocol_adapter_5_subscription_op_data *subscribe_data) { +static void s_aws_mqtt_protocol_adapter_5_subscription_op_data_delete( + struct aws_mqtt_protocol_adapter_5_subscription_op_data *subscribe_data) { aws_weak_ref_release(subscribe_data->callback_ref); aws_byte_buf_clean_up(&subscribe_data->topic_filter); @@ -90,9 +97,10 @@ static void s_aws_mqtt_protocol_adapter_5_subscription_op_data_delete(struct aws /* Subscribe */ -static void s_protocol_adapter_5_subscribe_completion(const struct aws_mqtt5_packet_suback_view *suback, - int error_code, - void *complete_ctx) { +static void s_protocol_adapter_5_subscribe_completion( + const struct aws_mqtt5_packet_suback_view *suback, + int error_code, + void *complete_ctx) { struct aws_mqtt_protocol_adapter_5_subscription_op_data *subscribe_data = complete_ctx; struct aws_mqtt_protocol_adapter_5_impl *adapter = aws_weak_ref_get_reference(subscribe_data->callback_ref); @@ -100,7 +108,8 @@ static void s_protocol_adapter_5_subscribe_completion(const struct aws_mqtt5_pac goto done; } - bool success = error_code == AWS_ERROR_SUCCESS && suback != NULL && suback->reason_code_count == 1 && suback->reason_codes[0] <= AWS_MQTT5_SARC_GRANTED_QOS_2; + bool success = error_code == AWS_ERROR_SUCCESS && suback != NULL && suback->reason_code_count == 1 && + suback->reason_codes[0] <= AWS_MQTT5_SARC_GRANTED_QOS_2; struct aws_protocol_adapter_subscription_event subscribe_event = { .topic_filter = aws_byte_cursor_from_buf(&subscribe_data->topic_filter), @@ -117,7 +126,9 @@ static void s_protocol_adapter_5_subscribe_completion(const struct aws_mqtt5_pac int s_aws_mqtt_protocol_adapter_5_subscribe(void *impl, struct aws_protocol_adapter_subscribe_options *options) { struct aws_mqtt_protocol_adapter_5_impl *adapter = impl; - struct aws_mqtt_protocol_adapter_5_subscription_op_data *subscribe_data = s_aws_mqtt_protocol_adapter_5_subscription_op_data_new(adapter->allocator, options->topic_filter, adapter->callback_ref); + struct aws_mqtt_protocol_adapter_5_subscription_op_data *subscribe_data = + s_aws_mqtt_protocol_adapter_5_subscription_op_data_new( + adapter->allocator, options->topic_filter, adapter->callback_ref); struct aws_mqtt5_subscription_view subscription_view = { .qos = AWS_MQTT5_QOS_AT_LEAST_ONCE, @@ -150,9 +161,10 @@ int s_aws_mqtt_protocol_adapter_5_subscribe(void *impl, struct aws_protocol_adap /* Unsubscribe */ -static void s_protocol_adapter_5_unsubscribe_completion(const struct aws_mqtt5_packet_unsuback_view *unsuback, - int error_code, - void *complete_ctx) { +static void s_protocol_adapter_5_unsubscribe_completion( + const struct aws_mqtt5_packet_unsuback_view *unsuback, + int error_code, + void *complete_ctx) { struct aws_mqtt_protocol_adapter_5_subscription_op_data *unsubscribe_data = complete_ctx; struct aws_mqtt_protocol_adapter_5_impl *adapter = aws_weak_ref_get_reference(unsubscribe_data->callback_ref); @@ -160,7 +172,8 @@ static void s_protocol_adapter_5_unsubscribe_completion(const struct aws_mqtt5_p goto done; } - bool success = error_code == AWS_ERROR_SUCCESS && unsuback != NULL && unsuback->reason_code_count == 1 && unsuback->reason_codes[0] < 128; + bool success = error_code == AWS_ERROR_SUCCESS && unsuback != NULL && unsuback->reason_code_count == 1 && + unsuback->reason_codes[0] < 128; struct aws_protocol_adapter_subscription_event unsubscribe_event = { .topic_filter = aws_byte_cursor_from_buf(&unsubscribe_data->topic_filter), @@ -177,7 +190,9 @@ static void s_protocol_adapter_5_unsubscribe_completion(const struct aws_mqtt5_p int s_aws_mqtt_protocol_adapter_5_unsubscribe(void *impl, struct aws_protocol_adapter_unsubscribe_options *options) { struct aws_mqtt_protocol_adapter_5_impl *adapter = impl; - struct aws_mqtt_protocol_adapter_5_subscription_op_data *unsubscribe_data = s_aws_mqtt_protocol_adapter_5_subscription_op_data_new(adapter->allocator, options->topic_filter, adapter->callback_ref); + struct aws_mqtt_protocol_adapter_5_subscription_op_data *unsubscribe_data = + s_aws_mqtt_protocol_adapter_5_subscription_op_data_new( + adapter->allocator, options->topic_filter, adapter->callback_ref); struct aws_mqtt5_packet_unsubscribe_view unsubscribe_view = { .topic_filters = &options->topic_filter, @@ -213,8 +228,12 @@ struct aws_mqtt_protocol_adapter_5_publish_op_data { void *user_data; }; -static struct aws_mqtt_protocol_adapter_5_publish_op_data *s_aws_mqtt_protocol_adapter_5_publish_op_data_new(struct aws_allocator *allocator, const struct aws_protocol_adapter_publish_options *publish_options, struct aws_weak_ref *callback_ref) { - struct aws_mqtt_protocol_adapter_5_publish_op_data *publish_data = aws_mem_calloc(allocator, 1, sizeof(struct aws_mqtt_protocol_adapter_5_publish_op_data)); +static struct aws_mqtt_protocol_adapter_5_publish_op_data *s_aws_mqtt_protocol_adapter_5_publish_op_data_new( + struct aws_allocator *allocator, + const struct aws_protocol_adapter_publish_options *publish_options, + struct aws_weak_ref *callback_ref) { + struct aws_mqtt_protocol_adapter_5_publish_op_data *publish_data = + aws_mem_calloc(allocator, 1, sizeof(struct aws_mqtt_protocol_adapter_5_publish_op_data)); publish_data->allocator = allocator; publish_data->callback_ref = aws_weak_ref_acquire(callback_ref); @@ -224,7 +243,8 @@ static struct aws_mqtt_protocol_adapter_5_publish_op_data *s_aws_mqtt_protocol_a return publish_data; } -static void s_aws_mqtt_protocol_adapter_5_publish_op_data_delete(struct aws_mqtt_protocol_adapter_5_publish_op_data *publish_data) { +static void s_aws_mqtt_protocol_adapter_5_publish_op_data_delete( + struct aws_mqtt_protocol_adapter_5_publish_op_data *publish_data) { aws_weak_ref_release(publish_data->callback_ref); aws_mem_release(publish_data->allocator, publish_data); @@ -259,13 +279,11 @@ static void s_protocol_adapter_5_publish_completion( int s_aws_mqtt_protocol_adapter_5_publish(void *impl, struct aws_protocol_adapter_publish_options *options) { struct aws_mqtt_protocol_adapter_5_impl *adapter = impl; - struct aws_mqtt_protocol_adapter_5_publish_op_data *publish_data = s_aws_mqtt_protocol_adapter_5_publish_op_data_new(adapter->allocator, options, adapter->callback_ref); + struct aws_mqtt_protocol_adapter_5_publish_op_data *publish_data = + s_aws_mqtt_protocol_adapter_5_publish_op_data_new(adapter->allocator, options, adapter->callback_ref); struct aws_mqtt5_packet_publish_view publish_view = { - .topic = options->topic, - .qos = AWS_MQTT5_QOS_AT_LEAST_ONCE, - .payload = options->payload - }; + .topic = options->topic, .qos = AWS_MQTT5_QOS_AT_LEAST_ONCE, .payload = options->payload}; struct aws_mqtt5_publish_completion_options completion_options = { .ack_timeout_seconds_override = options->ack_timeout_seconds, @@ -286,13 +304,13 @@ int s_aws_mqtt_protocol_adapter_5_publish(void *impl, struct aws_protocol_adapte return AWS_OP_ERR; } -static bool s_protocol_adapter_mqtt5_listener_publish_received(const struct aws_mqtt5_packet_publish_view *publish, void *user_data) { +static bool s_protocol_adapter_mqtt5_listener_publish_received( + const struct aws_mqtt5_packet_publish_view *publish, + void *user_data) { struct aws_mqtt_protocol_adapter_5_impl *adapter = user_data; struct aws_protocol_adapter_incoming_publish_event publish_event = { - .topic = publish->topic, - .payload = publish->payload - }; + .topic = publish->topic, .payload = publish->payload}; (*adapter->config.incoming_publish_callback)(&publish_event, adapter->config.user_data); @@ -346,8 +364,12 @@ static struct aws_mqtt_protocol_adapter_vtable s_protocol_adapter_mqtt5_vtable = .aws_mqtt_protocol_adapter_publish_fn = s_aws_mqtt_protocol_adapter_5_publish, }; -struct aws_mqtt_protocol_adapter *aws_mqtt_protocol_adapter_new_from_5(struct aws_allocator *allocator, struct aws_mqtt_protocol_adapter_options *options, struct aws_mqtt5_client *client) { - struct aws_mqtt_protocol_adapter_5_impl *adapter = aws_mem_calloc(allocator, 1, sizeof(struct aws_mqtt_protocol_adapter_5_impl)); +struct aws_mqtt_protocol_adapter *aws_mqtt_protocol_adapter_new_from_5( + struct aws_allocator *allocator, + struct aws_mqtt_protocol_adapter_options *options, + struct aws_mqtt5_client *client) { + struct aws_mqtt_protocol_adapter_5_impl *adapter = + aws_mem_calloc(allocator, 1, sizeof(struct aws_mqtt_protocol_adapter_5_impl)); adapter->allocator = allocator; adapter->base.impl = adapter; @@ -359,12 +381,11 @@ struct aws_mqtt_protocol_adapter *aws_mqtt_protocol_adapter_new_from_5(struct aw struct aws_mqtt5_listener_config listener_options = { .client = client, - .listener_callbacks = { - .listener_publish_received_handler = s_protocol_adapter_mqtt5_listener_publish_received, - .listener_publish_received_handler_user_data = adapter, - .lifecycle_event_handler = s_protocol_adapter_mqtt5_lifecycle_event_callback, - .lifecycle_event_handler_user_data = adapter - }, + .listener_callbacks = + {.listener_publish_received_handler = s_protocol_adapter_mqtt5_listener_publish_received, + .listener_publish_received_handler_user_data = adapter, + .lifecycle_event_handler = s_protocol_adapter_mqtt5_lifecycle_event_callback, + .lifecycle_event_handler_user_data = adapter}, .termination_callback = s_protocol_adapter_mqtt5_listener_termination_callback, .termination_callback_user_data = adapter, }; @@ -378,14 +399,20 @@ void aws_mqtt_protocol_adapter_delete(struct aws_mqtt_protocol_adapter *adapter) (*adapter->vtable->aws_mqtt_protocol_adapter_delete_fn)(adapter->impl); } -int aws_mqtt_protocol_adapter_subscribe(struct aws_mqtt_protocol_adapter *adapter, struct aws_protocol_adapter_subscribe_options *options) { +int aws_mqtt_protocol_adapter_subscribe( + struct aws_mqtt_protocol_adapter *adapter, + struct aws_protocol_adapter_subscribe_options *options) { return (*adapter->vtable->aws_mqtt_protocol_adapter_subscribe_fn)(adapter->impl, options); } -int aws_mqtt_protocol_adapter_unsubscribe(struct aws_mqtt_protocol_adapter *adapter, struct aws_protocol_adapter_unsubscribe_options *options) { +int aws_mqtt_protocol_adapter_unsubscribe( + struct aws_mqtt_protocol_adapter *adapter, + struct aws_protocol_adapter_unsubscribe_options *options) { return (*adapter->vtable->aws_mqtt_protocol_adapter_unsubscribe_fn)(adapter->impl, options); } -int aws_mqtt_protocol_adapter_publish(struct aws_mqtt_protocol_adapter *adapter, struct aws_protocol_adapter_publish_options *options) { +int aws_mqtt_protocol_adapter_publish( + struct aws_mqtt_protocol_adapter *adapter, + struct aws_protocol_adapter_publish_options *options) { return (*adapter->vtable->aws_mqtt_protocol_adapter_publish_fn)(adapter->impl, options); } diff --git a/source/request-response/weak_ref.c b/source/request-response/weak_ref.c index 97561ea9..949014e7 100644 --- a/source/request-response/weak_ref.c +++ b/source/request-response/weak_ref.c @@ -1,7 +1,7 @@ /** -* Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. -* SPDX-License-Identifier: Apache-2.0. -*/ + * Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. + * SPDX-License-Identifier: Apache-2.0. + */ #include diff --git a/tests/CMakeLists.txt b/tests/CMakeLists.txt index 500cf8fb..587495c4 100644 --- a/tests/CMakeLists.txt +++ b/tests/CMakeLists.txt @@ -446,6 +446,17 @@ add_test_case(request_response_mqtt5_protocol_adapter_subscribe_success) add_test_case(request_response_mqtt5_protocol_adapter_subscribe_failure_error_code) add_test_case(request_response_mqtt5_protocol_adapter_subscribe_failure_reason_code) add_test_case(request_response_mqtt5_protocol_adapter_subscribe_failure_timeout) +add_test_case(request_response_mqtt5_protocol_adapter_unsubscribe_success) +add_test_case(request_response_mqtt5_protocol_adapter_unsubscribe_failure_error_code) +add_test_case(request_response_mqtt5_protocol_adapter_unsubscribe_failure_reason_code) +add_test_case(request_response_mqtt5_protocol_adapter_unsubscribe_failure_timeout) +add_test_case(request_response_mqtt5_protocol_adapter_publish_success) +add_test_case(request_response_mqtt5_protocol_adapter_publish_failure_error_code) +add_test_case(request_response_mqtt5_protocol_adapter_publish_failure_reason_code) +add_test_case(request_response_mqtt5_protocol_adapter_publish_failure_timeout) +add_test_case(request_response_mqtt5_protocol_adapter_connection_event_sequence) +add_test_case(request_response_mqtt5_protocol_adapter_incoming_publish) +add_test_case(request_response_mqtt5_protocol_adapter_shutdown_while_pending) generate_test_driver(${PROJECT_NAME}-tests) diff --git a/tests/request_response_protocol_adapter_tests.c b/tests/request_response_protocol_adapter_tests.c index a5d3d1e1..572c8345 100644 --- a/tests/request_response_protocol_adapter_tests.c +++ b/tests/request_response_protocol_adapter_tests.c @@ -1,7 +1,7 @@ /** -* Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. -* SPDX-License-Identifier: Apache-2.0. -*/ + * Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. + * SPDX-License-Identifier: Apache-2.0. + */ #include "v5/mqtt5_testing_utils.h" #include @@ -25,7 +25,8 @@ static void s_request_response_protocol_adapter_incoming_publish_event_record_in aws_byte_buf_init_copy_from_cursor(&record->payload, allocator, payload); } -static void s_request_response_protocol_adapter_incoming_publish_event_record_cleanup(struct request_response_protocol_adapter_incoming_publish_event_record *record) { +static void s_request_response_protocol_adapter_incoming_publish_event_record_clean_up( + struct request_response_protocol_adapter_incoming_publish_event_record *record) { aws_byte_buf_clean_up(&record->topic); aws_byte_buf_clean_up(&record->payload); } @@ -48,7 +49,8 @@ static void s_request_response_protocol_adapter_subscription_event_record_init( aws_byte_buf_init_copy_from_cursor(&record->topic_filter, allocator, topic_filter); } -static void s_request_response_protocol_adapter_subscription_event_record_cleanup(struct request_response_protocol_adapter_subscription_event_record *record) { +static void s_request_response_protocol_adapter_subscription_event_record_cleanup( + struct request_response_protocol_adapter_subscription_event_record *record) { aws_byte_buf_clean_up(&record->topic_filter); } @@ -61,6 +63,7 @@ struct aws_request_response_mqtt5_adapter_test_fixture { struct aws_array_list incoming_publish_events; struct aws_array_list connection_events; struct aws_array_list subscription_events; + struct aws_array_list publish_results; bool adapter_terminated; @@ -68,14 +71,14 @@ struct aws_request_response_mqtt5_adapter_test_fixture { struct aws_condition_variable signal; }; - -static void s_rr_mqtt5_protocol_adapter_test_on_subscription_event(struct aws_protocol_adapter_subscription_event *event, void *user_data) { +static void s_rr_mqtt5_protocol_adapter_test_on_subscription_event( + struct aws_protocol_adapter_subscription_event *event, + void *user_data) { struct aws_request_response_mqtt5_adapter_test_fixture *fixture = user_data; - struct request_response_protocol_adapter_subscription_event_record record = { - .event_type = event->event_type - }; - s_request_response_protocol_adapter_subscription_event_record_init(&record, fixture->allocator, event->topic_filter); + struct request_response_protocol_adapter_subscription_event_record record = {.event_type = event->event_type}; + s_request_response_protocol_adapter_subscription_event_record_init( + &record, fixture->allocator, event->topic_filter); aws_mutex_lock(&fixture->lock); aws_array_list_push_back(&fixture->subscription_events, &record); @@ -83,12 +86,15 @@ static void s_rr_mqtt5_protocol_adapter_test_on_subscription_event(struct aws_pr aws_condition_variable_notify_all(&fixture->signal); } -static void s_rr_mqtt5_protocol_adapter_test_on_incoming_publish(struct aws_protocol_adapter_incoming_publish_event *publish, void *user_data) { +static void s_rr_mqtt5_protocol_adapter_test_on_incoming_publish( + struct aws_protocol_adapter_incoming_publish_event *publish, + void *user_data) { struct aws_request_response_mqtt5_adapter_test_fixture *fixture = user_data; struct request_response_protocol_adapter_incoming_publish_event_record record; AWS_ZERO_STRUCT(record); - s_request_response_protocol_adapter_incoming_publish_event_record_init(&record, fixture->allocator, publish->topic, publish->payload); + s_request_response_protocol_adapter_incoming_publish_event_record_init( + &record, fixture->allocator, publish->topic, publish->payload); aws_mutex_lock(&fixture->lock); aws_array_list_push_back(&fixture->incoming_publish_events, &record); @@ -105,13 +111,13 @@ static void s_rr_mqtt5_protocol_adapter_test_on_terminate_callback(void *user_da aws_condition_variable_notify_all(&fixture->signal); } -static void s_rr_mqtt5_protocol_adapter_test_on_connection_event(struct aws_protocol_adapter_connection_event *event, void *user_data) { +static void s_rr_mqtt5_protocol_adapter_test_on_connection_event( + struct aws_protocol_adapter_connection_event *event, + void *user_data) { struct aws_request_response_mqtt5_adapter_test_fixture *fixture = user_data; struct request_response_protocol_adapter_connection_event_record record = { - .event_type = event->event_type, - .rejoined_session = event->rejoined_session - }; + .event_type = event->event_type, .rejoined_session = event->rejoined_session}; aws_mutex_lock(&fixture->lock); aws_array_list_push_back(&fixture->connection_events, &record); @@ -119,6 +125,15 @@ static void s_rr_mqtt5_protocol_adapter_test_on_connection_event(struct aws_prot aws_condition_variable_notify_all(&fixture->signal); } +static void s_rr_mqtt5_protocol_adapter_test_on_publish_result(bool success, void *user_data) { + struct aws_request_response_mqtt5_adapter_test_fixture *fixture = user_data; + + aws_mutex_lock(&fixture->lock); + aws_array_list_push_back(&fixture->publish_results, &success); + aws_mutex_unlock(&fixture->lock); + aws_condition_variable_notify_all(&fixture->signal); +} + static int s_aws_request_response_mqtt5_adapter_test_fixture_init( struct aws_request_response_mqtt5_adapter_test_fixture *fixture, struct aws_allocator *allocator, @@ -137,15 +152,28 @@ static int s_aws_request_response_mqtt5_adapter_test_fixture_init( .incoming_publish_callback = s_rr_mqtt5_protocol_adapter_test_on_incoming_publish, .terminate_callback = s_rr_mqtt5_protocol_adapter_test_on_terminate_callback, .connection_event_callback = s_rr_mqtt5_protocol_adapter_test_on_connection_event, - .user_data = fixture - }; + .user_data = fixture}; - fixture->protocol_adapter = aws_mqtt_protocol_adapter_new_from_5(allocator, &protocol_adapter_options, fixture->mqtt5_fixture.client); + fixture->protocol_adapter = + aws_mqtt_protocol_adapter_new_from_5(allocator, &protocol_adapter_options, fixture->mqtt5_fixture.client); AWS_FATAL_ASSERT(fixture->protocol_adapter != NULL); - aws_array_list_init_dynamic(&fixture->incoming_publish_events, allocator, 10, sizeof(struct request_response_protocol_adapter_incoming_publish_event_record)); - aws_array_list_init_dynamic(&fixture->connection_events, allocator, 10, sizeof(struct request_response_protocol_adapter_connection_event_record)); - aws_array_list_init_dynamic(&fixture->subscription_events, allocator, 10, sizeof(struct request_response_protocol_adapter_subscription_event_record)); + aws_array_list_init_dynamic( + &fixture->incoming_publish_events, + allocator, + 10, + sizeof(struct request_response_protocol_adapter_incoming_publish_event_record)); + aws_array_list_init_dynamic( + &fixture->connection_events, + allocator, + 10, + sizeof(struct request_response_protocol_adapter_connection_event_record)); + aws_array_list_init_dynamic( + &fixture->subscription_events, + allocator, + 10, + sizeof(struct request_response_protocol_adapter_subscription_event_record)); + aws_array_list_init_dynamic(&fixture->publish_results, allocator, 10, sizeof(bool)); aws_mutex_init(&fixture->lock); aws_condition_variable_init(&fixture->signal); @@ -159,12 +187,22 @@ static bool s_is_adapter_terminated(void *context) { return fixture->adapter_terminated; } -static void s_aws_request_response_mqtt5_adapter_test_fixture_clean_up(struct aws_request_response_mqtt5_adapter_test_fixture *fixture) { - aws_mqtt_protocol_adapter_delete(fixture->protocol_adapter); +static void s_aws_request_response_mqtt5_adapter_test_fixture_destroy_adapters( + struct aws_request_response_mqtt5_adapter_test_fixture *fixture) { + if (fixture->protocol_adapter != NULL) { + aws_mqtt_protocol_adapter_delete(fixture->protocol_adapter); - aws_mutex_lock(&fixture->lock); - aws_condition_variable_wait_pred(&fixture->signal, &fixture->lock, s_is_adapter_terminated, fixture); - aws_mutex_unlock(&fixture->lock); + aws_mutex_lock(&fixture->lock); + aws_condition_variable_wait_pred(&fixture->signal, &fixture->lock, s_is_adapter_terminated, fixture); + aws_mutex_unlock(&fixture->lock); + fixture->protocol_adapter = NULL; + } +} + +static void s_aws_request_response_mqtt5_adapter_test_fixture_clean_up( + struct aws_request_response_mqtt5_adapter_test_fixture *fixture) { + + s_aws_request_response_mqtt5_adapter_test_fixture_destroy_adapters(fixture); aws_mqtt5_client_mock_test_fixture_clean_up(&fixture->mqtt5_fixture); @@ -178,11 +216,12 @@ static void s_aws_request_response_mqtt5_adapter_test_fixture_clean_up(struct aw for (size_t i = 0; i < aws_array_list_length(&fixture->incoming_publish_events); ++i) { struct request_response_protocol_adapter_incoming_publish_event_record record; aws_array_list_get_at(&fixture->incoming_publish_events, &record, i); - s_request_response_protocol_adapter_incoming_publish_event_record_cleanup(&record); + s_request_response_protocol_adapter_incoming_publish_event_record_clean_up(&record); } aws_array_list_clean_up(&fixture->incoming_publish_events); aws_array_list_clean_up(&fixture->connection_events); + aws_array_list_clean_up(&fixture->publish_results); aws_mutex_clean_up(&fixture->lock); aws_condition_variable_clean_up(&fixture->signal); @@ -206,7 +245,8 @@ static bool s_do_subscription_events_contain(void *context) { if (record.event_type == wait_context->expected_event->event_type) { struct aws_byte_cursor record_topic_filter = aws_byte_cursor_from_buf(&record.topic_filter); - struct aws_byte_cursor expected_topic_filter = aws_byte_cursor_from_buf(&wait_context->expected_event->topic_filter); + struct aws_byte_cursor expected_topic_filter = + aws_byte_cursor_from_buf(&wait_context->expected_event->topic_filter); if (aws_byte_cursor_eq(&record_topic_filter, &expected_topic_filter)) { ++found; } @@ -216,9 +256,10 @@ static bool s_do_subscription_events_contain(void *context) { return found >= wait_context->expected_count; } -static void s_wait_for_subscription_events_contains(struct aws_request_response_mqtt5_adapter_test_fixture *fixture, - struct request_response_protocol_adapter_subscription_event_record *expected_event, - size_t expected_count) { +static void s_wait_for_subscription_events_contains( + struct aws_request_response_mqtt5_adapter_test_fixture *fixture, + struct request_response_protocol_adapter_subscription_event_record *expected_event, + size_t expected_count) { struct test_subscription_event_wait_context context = { .expected_event = expected_event, @@ -247,7 +288,8 @@ static bool s_do_connection_events_contain(void *context) { struct request_response_protocol_adapter_connection_event_record record; aws_array_list_get_at(&wait_context->fixture->connection_events, &record, i); - if (record.event_type == wait_context->expected_event->event_type && record.rejoined_session == wait_context->expected_event->rejoined_session) { + if (record.event_type == wait_context->expected_event->event_type && + record.rejoined_session == wait_context->expected_event->rejoined_session) { ++found; } } @@ -255,9 +297,10 @@ static bool s_do_connection_events_contain(void *context) { return found >= wait_context->expected_count; } -static void s_wait_for_connection_events_contains(struct aws_request_response_mqtt5_adapter_test_fixture *fixture, - struct request_response_protocol_adapter_connection_event_record *expected_event, - size_t expected_count) { +static void s_wait_for_connection_events_contains( + struct aws_request_response_mqtt5_adapter_test_fixture *fixture, + struct request_response_protocol_adapter_connection_event_record *expected_event, + size_t expected_count) { struct test_connection_event_wait_context context = { .expected_event = expected_event, @@ -304,9 +347,10 @@ static bool s_do_incoming_publish_events_contain(void *context) { return found >= wait_context->expected_count; } -static void s_wait_for_incoming_publish_events_contains(struct aws_request_response_mqtt5_adapter_test_fixture *fixture, - struct request_response_protocol_adapter_incoming_publish_event_record *expected_event, - size_t expected_count) { +static void s_wait_for_incoming_publish_events_contains( + struct aws_request_response_mqtt5_adapter_test_fixture *fixture, + struct request_response_protocol_adapter_incoming_publish_event_record *expected_event, + size_t expected_count) { struct test_incoming_publish_event_wait_context context = { .expected_event = expected_event, @@ -319,6 +363,46 @@ static void s_wait_for_incoming_publish_events_contains(struct aws_request_respo aws_mutex_unlock(&fixture->lock); } +struct test_publish_result_wait_context { + bool expected_success; + size_t expected_count; + struct aws_request_response_mqtt5_adapter_test_fixture *fixture; +}; + +static bool s_do_publish_results_contain(void *context) { + struct test_publish_result_wait_context *wait_context = context; + + size_t found = 0; + + size_t num_events = aws_array_list_length(&wait_context->fixture->publish_results); + for (size_t i = 0; i < num_events; ++i) { + bool success = false; + aws_array_list_get_at(&wait_context->fixture->publish_results, &success, i); + + if (success == wait_context->expected_success) { + ++found; + } + } + + return found >= wait_context->expected_count; +} + +static void s_wait_for_publish_results_contains( + struct aws_request_response_mqtt5_adapter_test_fixture *fixture, + bool success, + size_t expected_count) { + + struct test_publish_result_wait_context context = { + .expected_success = success, + .expected_count = expected_count, + .fixture = fixture, + }; + + aws_mutex_lock(&fixture->lock); + aws_condition_variable_wait_pred(&fixture->signal, &fixture->lock, s_do_publish_results_contain, &context); + aws_mutex_unlock(&fixture->lock); +} + enum protocol_adapter_operation_test_type { PAOTT_SUCCESS, PAOTT_FAILURE_TIMEOUT, @@ -348,7 +432,9 @@ static int s_aws_mqtt5_server_send_failed_suback_on_subscribe( return aws_mqtt5_mock_server_send_packet(connection, AWS_MQTT5_PT_SUBACK, &suback_view); } -static int s_do_request_response_mqtt5_protocol_adapter_subscribe_test(struct aws_allocator *allocator, enum protocol_adapter_operation_test_type test_type) { +static int s_do_request_response_mqtt5_protocol_adapter_subscribe_test( + struct aws_allocator *allocator, + enum protocol_adapter_operation_test_type test_type) { aws_mqtt_library_init(allocator); struct mqtt5_client_test_options test_options; @@ -372,7 +458,8 @@ static int s_do_request_response_mqtt5_protocol_adapter_subscribe_test(struct aw }; struct aws_request_response_mqtt5_adapter_test_fixture fixture; - ASSERT_SUCCESS(s_aws_request_response_mqtt5_adapter_test_fixture_init(&fixture, allocator, &mqtt5_test_fixture_options)); + ASSERT_SUCCESS( + s_aws_request_response_mqtt5_adapter_test_fixture_init(&fixture, allocator, &mqtt5_test_fixture_options)); struct aws_mqtt5_client *client = fixture.mqtt5_fixture.client; @@ -386,7 +473,8 @@ static int s_do_request_response_mqtt5_protocol_adapter_subscribe_test(struct aw .event_type = (test_type == PAOTT_SUCCESS) ? AWS_PASET_SUBSCRIBE_SUCCESS : AWS_PASET_SUBSCRIBE_FAILURE, }; - aws_byte_buf_init_copy_from_cursor(&expected_outcome.topic_filter, allocator, aws_byte_cursor_from_c_str("hello/world")); + aws_byte_buf_init_copy_from_cursor( + &expected_outcome.topic_filter, allocator, aws_byte_cursor_from_c_str("hello/world")); struct aws_protocol_adapter_subscribe_options subscribe_options = { .topic_filter = aws_byte_cursor_from_buf(&expected_outcome.topic_filter), @@ -418,7 +506,9 @@ AWS_TEST_CASE( request_response_mqtt5_protocol_adapter_subscribe_success, s_request_response_mqtt5_protocol_adapter_subscribe_success_fn) -static int s_request_response_mqtt5_protocol_adapter_subscribe_failure_timeout_fn(struct aws_allocator *allocator, void *ctx) { +static int s_request_response_mqtt5_protocol_adapter_subscribe_failure_timeout_fn( + struct aws_allocator *allocator, + void *ctx) { (void)ctx; ASSERT_SUCCESS(s_do_request_response_mqtt5_protocol_adapter_subscribe_test(allocator, PAOTT_FAILURE_TIMEOUT)); @@ -430,7 +520,9 @@ AWS_TEST_CASE( request_response_mqtt5_protocol_adapter_subscribe_failure_timeout, s_request_response_mqtt5_protocol_adapter_subscribe_failure_timeout_fn) -static int s_request_response_mqtt5_protocol_adapter_subscribe_failure_reason_code_fn(struct aws_allocator *allocator, void *ctx) { +static int s_request_response_mqtt5_protocol_adapter_subscribe_failure_reason_code_fn( + struct aws_allocator *allocator, + void *ctx) { (void)ctx; ASSERT_SUCCESS(s_do_request_response_mqtt5_protocol_adapter_subscribe_test(allocator, PAOTT_FAILURE_REASON_CODE)); @@ -442,7 +534,9 @@ AWS_TEST_CASE( request_response_mqtt5_protocol_adapter_subscribe_failure_reason_code, s_request_response_mqtt5_protocol_adapter_subscribe_failure_reason_code_fn) -static int s_request_response_mqtt5_protocol_adapter_subscribe_failure_error_code_fn(struct aws_allocator *allocator, void *ctx) { +static int s_request_response_mqtt5_protocol_adapter_subscribe_failure_error_code_fn( + struct aws_allocator *allocator, + void *ctx) { (void)ctx; ASSERT_SUCCESS(s_do_request_response_mqtt5_protocol_adapter_subscribe_test(allocator, PAOTT_FAILURE_ERROR_CODE)); @@ -453,3 +547,475 @@ static int s_request_response_mqtt5_protocol_adapter_subscribe_failure_error_cod AWS_TEST_CASE( request_response_mqtt5_protocol_adapter_subscribe_failure_error_code, s_request_response_mqtt5_protocol_adapter_subscribe_failure_error_code_fn) + +static enum aws_mqtt5_unsuback_reason_code s_failed_unsuback_reason_codes[] = { + AWS_MQTT5_UARC_NOT_AUTHORIZED, +}; + +static int s_aws_mqtt5_server_send_failed_unsuback_on_unsubscribe( + void *packet, + struct aws_mqtt5_server_mock_connection_context *connection, + void *user_data) { + (void)packet; + (void)user_data; + + struct aws_mqtt5_packet_subscribe_view *unsubscribe_view = packet; + + struct aws_mqtt5_packet_unsuback_view unsuback_view = { + .packet_id = unsubscribe_view->packet_id, + .reason_code_count = AWS_ARRAY_SIZE(s_failed_unsuback_reason_codes), + .reason_codes = s_failed_unsuback_reason_codes, + }; + + return aws_mqtt5_mock_server_send_packet(connection, AWS_MQTT5_PT_UNSUBACK, &unsuback_view); +} + +static int s_do_request_response_mqtt5_protocol_adapter_unsubscribe_test( + struct aws_allocator *allocator, + enum protocol_adapter_operation_test_type test_type) { + aws_mqtt_library_init(allocator); + + struct mqtt5_client_test_options test_options; + aws_mqtt5_client_test_init_default_options(&test_options); + + if (test_type == PAOTT_SUCCESS) { + test_options.server_function_table.packet_handlers[AWS_MQTT5_PT_UNSUBSCRIBE] = + aws_mqtt5_mock_server_handle_unsubscribe_unsuback_success; + } else if (test_type == PAOTT_FAILURE_REASON_CODE) { + test_options.server_function_table.packet_handlers[AWS_MQTT5_PT_UNSUBSCRIBE] = + s_aws_mqtt5_server_send_failed_unsuback_on_unsubscribe; + } + + if (test_type == PAOTT_FAILURE_ERROR_CODE) { + test_options.client_options.offline_queue_behavior = AWS_MQTT5_COQBT_FAIL_ALL_ON_DISCONNECT; + } + + struct aws_mqtt5_client_mqtt5_mock_test_fixture_options mqtt5_test_fixture_options = { + .client_options = &test_options.client_options, + .server_function_table = &test_options.server_function_table, + }; + + struct aws_request_response_mqtt5_adapter_test_fixture fixture; + ASSERT_SUCCESS( + s_aws_request_response_mqtt5_adapter_test_fixture_init(&fixture, allocator, &mqtt5_test_fixture_options)); + + struct aws_mqtt5_client *client = fixture.mqtt5_fixture.client; + + if (test_type != PAOTT_FAILURE_ERROR_CODE) { + ASSERT_SUCCESS(aws_mqtt5_client_start(client)); + + aws_wait_for_connected_lifecycle_event(&fixture.mqtt5_fixture); + } + + struct request_response_protocol_adapter_subscription_event_record expected_outcome = { + .event_type = (test_type == PAOTT_SUCCESS) ? AWS_PASET_UNSUBSCRIBE_SUCCESS : AWS_PASET_UNSUBSCRIBE_FAILURE, + }; + + aws_byte_buf_init_copy_from_cursor( + &expected_outcome.topic_filter, allocator, aws_byte_cursor_from_c_str("hello/world")); + + struct aws_protocol_adapter_unsubscribe_options unsubscribe_options = { + .topic_filter = aws_byte_cursor_from_buf(&expected_outcome.topic_filter), + .ack_timeout_seconds = 2, + }; + + aws_mqtt_protocol_adapter_unsubscribe(fixture.protocol_adapter, &unsubscribe_options); + + s_wait_for_subscription_events_contains(&fixture, &expected_outcome, 1); + + s_request_response_protocol_adapter_subscription_event_record_cleanup(&expected_outcome); + + s_aws_request_response_mqtt5_adapter_test_fixture_clean_up(&fixture); + + aws_mqtt_library_clean_up(); + + return AWS_OP_SUCCESS; +} + +static int s_request_response_mqtt5_protocol_adapter_unsubscribe_success_fn( + struct aws_allocator *allocator, + void *ctx) { + (void)ctx; + + ASSERT_SUCCESS(s_do_request_response_mqtt5_protocol_adapter_unsubscribe_test(allocator, PAOTT_SUCCESS)); + + return AWS_OP_SUCCESS; +} + +AWS_TEST_CASE( + request_response_mqtt5_protocol_adapter_unsubscribe_success, + s_request_response_mqtt5_protocol_adapter_unsubscribe_success_fn) + +static int s_request_response_mqtt5_protocol_adapter_unsubscribe_failure_timeout_fn( + struct aws_allocator *allocator, + void *ctx) { + (void)ctx; + + ASSERT_SUCCESS(s_do_request_response_mqtt5_protocol_adapter_unsubscribe_test(allocator, PAOTT_FAILURE_TIMEOUT)); + + return AWS_OP_SUCCESS; +} + +AWS_TEST_CASE( + request_response_mqtt5_protocol_adapter_unsubscribe_failure_timeout, + s_request_response_mqtt5_protocol_adapter_unsubscribe_failure_timeout_fn) + +static int s_request_response_mqtt5_protocol_adapter_unsubscribe_failure_reason_code_fn( + struct aws_allocator *allocator, + void *ctx) { + (void)ctx; + + ASSERT_SUCCESS(s_do_request_response_mqtt5_protocol_adapter_unsubscribe_test(allocator, PAOTT_FAILURE_REASON_CODE)); + + return AWS_OP_SUCCESS; +} + +AWS_TEST_CASE( + request_response_mqtt5_protocol_adapter_unsubscribe_failure_reason_code, + s_request_response_mqtt5_protocol_adapter_unsubscribe_failure_reason_code_fn) + +static int s_request_response_mqtt5_protocol_adapter_unsubscribe_failure_error_code_fn( + struct aws_allocator *allocator, + void *ctx) { + (void)ctx; + + ASSERT_SUCCESS(s_do_request_response_mqtt5_protocol_adapter_unsubscribe_test(allocator, PAOTT_FAILURE_ERROR_CODE)); + + return AWS_OP_SUCCESS; +} + +AWS_TEST_CASE( + request_response_mqtt5_protocol_adapter_unsubscribe_failure_error_code, + s_request_response_mqtt5_protocol_adapter_unsubscribe_failure_error_code_fn) + +static int s_aws_mqtt5_server_send_failed_puback_on_publish( + void *packet, + struct aws_mqtt5_server_mock_connection_context *connection, + void *user_data) { + (void)packet; + (void)user_data; + + struct aws_mqtt5_packet_publish_view *publish_view = packet; + + if (publish_view->qos == AWS_MQTT5_QOS_AT_LEAST_ONCE) { + struct aws_mqtt5_packet_puback_view puback_view = { + .packet_id = publish_view->packet_id, + .reason_code = AWS_MQTT5_PARC_NOT_AUTHORIZED, + }; + + return aws_mqtt5_mock_server_send_packet(connection, AWS_MQTT5_PT_PUBACK, &puback_view); + } + + return AWS_OP_SUCCESS; +} + +static int s_do_request_response_mqtt5_protocol_adapter_publish_test( + struct aws_allocator *allocator, + enum protocol_adapter_operation_test_type test_type) { + aws_mqtt_library_init(allocator); + + struct mqtt5_client_test_options test_options; + aws_mqtt5_client_test_init_default_options(&test_options); + + if (test_type == PAOTT_SUCCESS) { + test_options.server_function_table.packet_handlers[AWS_MQTT5_PT_PUBLISH] = + aws_mqtt5_mock_server_handle_publish_puback; + } else if (test_type == PAOTT_FAILURE_REASON_CODE) { + test_options.server_function_table.packet_handlers[AWS_MQTT5_PT_PUBLISH] = + s_aws_mqtt5_server_send_failed_puback_on_publish; + } + + if (test_type == PAOTT_FAILURE_ERROR_CODE) { + test_options.client_options.offline_queue_behavior = AWS_MQTT5_COQBT_FAIL_ALL_ON_DISCONNECT; + } + + struct aws_mqtt5_client_mqtt5_mock_test_fixture_options mqtt5_test_fixture_options = { + .client_options = &test_options.client_options, + .server_function_table = &test_options.server_function_table, + }; + + struct aws_request_response_mqtt5_adapter_test_fixture fixture; + ASSERT_SUCCESS( + s_aws_request_response_mqtt5_adapter_test_fixture_init(&fixture, allocator, &mqtt5_test_fixture_options)); + + struct aws_mqtt5_client *client = fixture.mqtt5_fixture.client; + + if (test_type != PAOTT_FAILURE_ERROR_CODE) { + ASSERT_SUCCESS(aws_mqtt5_client_start(client)); + + aws_wait_for_connected_lifecycle_event(&fixture.mqtt5_fixture); + } + + struct aws_protocol_adapter_publish_options publish_options = { + .topic = aws_byte_cursor_from_c_str("hello/world"), + .payload = aws_byte_cursor_from_c_str("SomePayload"), + .ack_timeout_seconds = 2, + .completion_callback_fn = s_rr_mqtt5_protocol_adapter_test_on_publish_result, + .user_data = &fixture}; + + aws_mqtt_protocol_adapter_publish(fixture.protocol_adapter, &publish_options); + + s_wait_for_publish_results_contains(&fixture, test_type == PAOTT_SUCCESS, 1); + + s_aws_request_response_mqtt5_adapter_test_fixture_clean_up(&fixture); + + aws_mqtt_library_clean_up(); + + return AWS_OP_SUCCESS; +} + +static int s_request_response_mqtt5_protocol_adapter_publish_success_fn(struct aws_allocator *allocator, void *ctx) { + (void)ctx; + + ASSERT_SUCCESS(s_do_request_response_mqtt5_protocol_adapter_publish_test(allocator, PAOTT_SUCCESS)); + + return AWS_OP_SUCCESS; +} + +AWS_TEST_CASE( + request_response_mqtt5_protocol_adapter_publish_success, + s_request_response_mqtt5_protocol_adapter_publish_success_fn) + +static int s_request_response_mqtt5_protocol_adapter_publish_failure_timeout_fn( + struct aws_allocator *allocator, + void *ctx) { + (void)ctx; + + ASSERT_SUCCESS(s_do_request_response_mqtt5_protocol_adapter_publish_test(allocator, PAOTT_FAILURE_TIMEOUT)); + + return AWS_OP_SUCCESS; +} + +AWS_TEST_CASE( + request_response_mqtt5_protocol_adapter_publish_failure_timeout, + s_request_response_mqtt5_protocol_adapter_publish_failure_timeout_fn) + +static int s_request_response_mqtt5_protocol_adapter_publish_failure_reason_code_fn( + struct aws_allocator *allocator, + void *ctx) { + (void)ctx; + + ASSERT_SUCCESS(s_do_request_response_mqtt5_protocol_adapter_publish_test(allocator, PAOTT_FAILURE_REASON_CODE)); + + return AWS_OP_SUCCESS; +} + +AWS_TEST_CASE( + request_response_mqtt5_protocol_adapter_publish_failure_reason_code, + s_request_response_mqtt5_protocol_adapter_publish_failure_reason_code_fn) + +static int s_request_response_mqtt5_protocol_adapter_publish_failure_error_code_fn( + struct aws_allocator *allocator, + void *ctx) { + (void)ctx; + + ASSERT_SUCCESS(s_do_request_response_mqtt5_protocol_adapter_publish_test(allocator, PAOTT_FAILURE_ERROR_CODE)); + + return AWS_OP_SUCCESS; +} + +AWS_TEST_CASE( + request_response_mqtt5_protocol_adapter_publish_failure_error_code, + s_request_response_mqtt5_protocol_adapter_publish_failure_error_code_fn) + +static int s_request_response_mqtt5_protocol_adapter_connection_event_sequence_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); + + test_options.server_function_table.packet_handlers[AWS_MQTT5_PT_CONNECT] = + aws_mqtt5_mock_server_handle_connect_honor_session_unconditional; + test_options.client_options.session_behavior = AWS_MQTT5_CSBT_REJOIN_POST_SUCCESS; + + struct aws_mqtt5_client_mqtt5_mock_test_fixture_options mqtt5_test_fixture_options = { + .client_options = &test_options.client_options, + .server_function_table = &test_options.server_function_table, + }; + + struct aws_request_response_mqtt5_adapter_test_fixture fixture; + ASSERT_SUCCESS( + s_aws_request_response_mqtt5_adapter_test_fixture_init(&fixture, allocator, &mqtt5_test_fixture_options)); + + struct aws_mqtt5_client *client = fixture.mqtt5_fixture.client; + + struct request_response_protocol_adapter_connection_event_record online_record1 = { + .event_type = AWS_PACET_ONLINE, + .rejoined_session = false, + }; + + ASSERT_SUCCESS(aws_mqtt5_client_start(client)); + s_wait_for_connection_events_contains(&fixture, &online_record1, 1); + + struct request_response_protocol_adapter_connection_event_record offline_record = { + .event_type = AWS_PACET_OFFLINE, + }; + + ASSERT_SUCCESS(aws_mqtt5_client_stop(client, NULL, NULL)); + s_wait_for_connection_events_contains(&fixture, &offline_record, 1); + + struct request_response_protocol_adapter_connection_event_record online_record2 = { + .event_type = AWS_PACET_ONLINE, + .rejoined_session = true, + }; + + ASSERT_SUCCESS(aws_mqtt5_client_start(client)); + s_wait_for_connection_events_contains(&fixture, &online_record2, 1); + + ASSERT_SUCCESS(aws_mqtt5_client_stop(client, NULL, NULL)); + s_wait_for_connection_events_contains(&fixture, &offline_record, 2); + + s_aws_request_response_mqtt5_adapter_test_fixture_clean_up(&fixture); + + aws_mqtt_library_clean_up(); + + return AWS_OP_SUCCESS; +} + +AWS_TEST_CASE( + request_response_mqtt5_protocol_adapter_connection_event_sequence, + s_request_response_mqtt5_protocol_adapter_connection_event_sequence_fn) + +static int s_request_response_mqtt5_protocol_adapter_incoming_publish_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); + + test_options.server_function_table.packet_handlers[AWS_MQTT5_PT_PUBLISH] = + aws_mqtt5_mock_server_handle_publish_puback_and_forward; + + struct aws_mqtt5_client_mqtt5_mock_test_fixture_options mqtt5_test_fixture_options = { + .client_options = &test_options.client_options, + .server_function_table = &test_options.server_function_table, + }; + + struct aws_request_response_mqtt5_adapter_test_fixture fixture; + ASSERT_SUCCESS( + s_aws_request_response_mqtt5_adapter_test_fixture_init(&fixture, allocator, &mqtt5_test_fixture_options)); + + struct aws_mqtt5_client *client = fixture.mqtt5_fixture.client; + + ASSERT_SUCCESS(aws_mqtt5_client_start(client)); + + aws_wait_for_connected_lifecycle_event(&fixture.mqtt5_fixture); + + struct request_response_protocol_adapter_incoming_publish_event_record expected_publish; + AWS_ZERO_STRUCT(expected_publish); + + s_request_response_protocol_adapter_incoming_publish_event_record_init( + &expected_publish, + allocator, + aws_byte_cursor_from_c_str("hello/world"), + aws_byte_cursor_from_c_str("SomePayload")); + + struct aws_protocol_adapter_publish_options publish_options = { + .topic = aws_byte_cursor_from_buf(&expected_publish.topic), + .payload = aws_byte_cursor_from_buf(&expected_publish.payload), + .ack_timeout_seconds = 2, + .completion_callback_fn = s_rr_mqtt5_protocol_adapter_test_on_publish_result, + .user_data = &fixture}; + + aws_mqtt_protocol_adapter_publish(fixture.protocol_adapter, &publish_options); + + s_wait_for_incoming_publish_events_contains(&fixture, &expected_publish, 1); + + s_request_response_protocol_adapter_incoming_publish_event_record_clean_up(&expected_publish); + + s_aws_request_response_mqtt5_adapter_test_fixture_clean_up(&fixture); + + aws_mqtt_library_clean_up(); + + return AWS_OP_SUCCESS; +} + +AWS_TEST_CASE( + request_response_mqtt5_protocol_adapter_incoming_publish, + s_request_response_mqtt5_protocol_adapter_incoming_publish_fn) + +static int s_request_response_mqtt5_protocol_adapter_shutdown_while_pending_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); + + // don't respond to anything + test_options.server_function_table.packet_handlers[AWS_MQTT5_PT_PUBLISH] = NULL; + test_options.server_function_table.packet_handlers[AWS_MQTT5_PT_SUBSCRIBE] = NULL; + test_options.server_function_table.packet_handlers[AWS_MQTT5_PT_UNSUBSCRIBE] = NULL; + + test_options.client_options.offline_queue_behavior = AWS_MQTT5_COQBT_FAIL_ALL_ON_DISCONNECT; + + struct aws_mqtt5_client_mqtt5_mock_test_fixture_options mqtt5_test_fixture_options = { + .client_options = &test_options.client_options, + .server_function_table = &test_options.server_function_table, + }; + + struct aws_request_response_mqtt5_adapter_test_fixture fixture; + ASSERT_SUCCESS( + s_aws_request_response_mqtt5_adapter_test_fixture_init(&fixture, allocator, &mqtt5_test_fixture_options)); + + struct aws_mqtt5_client *client = fixture.mqtt5_fixture.client; + + ASSERT_SUCCESS(aws_mqtt5_client_start(client)); + + aws_wait_for_connected_lifecycle_event(&fixture.mqtt5_fixture); + + // publish + struct aws_protocol_adapter_publish_options publish_options = { + .topic = aws_byte_cursor_from_c_str("hello/world"), + .payload = aws_byte_cursor_from_c_str("SomePayload"), + .ack_timeout_seconds = 5, + .completion_callback_fn = s_rr_mqtt5_protocol_adapter_test_on_publish_result, + .user_data = &fixture}; + + aws_mqtt_protocol_adapter_publish(fixture.protocol_adapter, &publish_options); + + // subscribe + struct aws_protocol_adapter_subscribe_options subscribe_options = { + .topic_filter = aws_byte_cursor_from_c_str("hello/world"), + .ack_timeout_seconds = 5, + }; + + aws_mqtt_protocol_adapter_subscribe(fixture.protocol_adapter, &subscribe_options); + + // unsubscribe + struct aws_protocol_adapter_unsubscribe_options unsubscribe_options = { + .topic_filter = aws_byte_cursor_from_c_str("hello/world"), + .ack_timeout_seconds = 5, + }; + + aws_mqtt_protocol_adapter_unsubscribe(fixture.protocol_adapter, &unsubscribe_options); + + // tear down the adapter, leaving the in-progress operations with nothing to call back into + s_aws_request_response_mqtt5_adapter_test_fixture_destroy_adapters(&fixture); + + // stop the mqtt client, which fails the pending MQTT operations + ASSERT_SUCCESS(aws_mqtt5_client_stop(client, NULL, NULL)); + + // wait for the stop to complete, which implies all the operations have been completed without calling back + // into a deleted adapter + aws_wait_for_n_lifecycle_events(&fixture.mqtt5_fixture, AWS_MQTT5_CLET_STOPPED, 1); + + // nothing to verify, we just don't want to crash + + s_aws_request_response_mqtt5_adapter_test_fixture_clean_up(&fixture); + + aws_mqtt_library_clean_up(); + + return AWS_OP_SUCCESS; +} + +AWS_TEST_CASE( + request_response_mqtt5_protocol_adapter_shutdown_while_pending, + s_request_response_mqtt5_protocol_adapter_shutdown_while_pending_fn) diff --git a/tests/v5/mqtt5_client_tests.c b/tests/v5/mqtt5_client_tests.c index 7049dbea..69159cb2 100644 --- a/tests/v5/mqtt5_client_tests.c +++ b/tests/v5/mqtt5_client_tests.c @@ -2710,7 +2710,7 @@ static int s_aws_mqtt5_mock_server_handle_connect_honor_session_after_success( return aws_mqtt5_mock_server_send_packet(connection, AWS_MQTT5_PT_CONNACK, &connack_view); } -static int s_aws_mqtt5_mock_server_handle_connect_honor_session_unconditional( +int aws_mqtt5_mock_server_handle_connect_honor_session_unconditional( void *packet, struct aws_mqtt5_server_mock_connection_context *connection, void *user_data) { @@ -2752,7 +2752,7 @@ static bool s_received_n_lifecycle_events(void *arg) { return matching_events >= context->expected_event_count; } -static void s_wait_for_n_lifecycle_events( +void aws_wait_for_n_lifecycle_events( struct aws_mqtt5_client_mock_test_fixture *test_fixture, enum aws_mqtt5_client_lifecycle_event_type event_type, size_t expected_event_count) { @@ -2809,7 +2809,7 @@ static int s_do_mqtt5_client_session_resumption_test( test_options.client_options.session_behavior = session_behavior; test_options.server_function_table.packet_handlers[AWS_MQTT5_PT_CONNECT] = - s_aws_mqtt5_mock_server_handle_connect_honor_session_unconditional; + aws_mqtt5_mock_server_handle_connect_honor_session_unconditional; struct aws_mqtt5_client_mqtt5_mock_test_fixture_options test_fixture_options = { .client_options = &test_options.client_options, @@ -2823,7 +2823,7 @@ static int s_do_mqtt5_client_session_resumption_test( for (size_t i = 0; i < SESSION_RESUMPTION_CONNECT_COUNT; ++i) { ASSERT_SUCCESS(aws_mqtt5_client_start(client)); - s_wait_for_n_lifecycle_events(&test_context, AWS_MQTT5_CLET_CONNECTION_SUCCESS, i + 1); + aws_wait_for_n_lifecycle_events(&test_context, AWS_MQTT5_CLET_CONNECTION_SUCCESS, i + 1); /* not technically truly safe to query depending on memory model. Remove if it becomes a problem. */ bool expected_rejoined_session = s_compute_expected_rejoined_session(session_behavior, i); @@ -2832,7 +2832,7 @@ static int s_do_mqtt5_client_session_resumption_test( /* can't use stop as that wipes session state */ aws_channel_shutdown(test_context.server_channel, AWS_ERROR_UNKNOWN); - s_wait_for_n_lifecycle_events(&test_context, AWS_MQTT5_CLET_DISCONNECTION, i + 1); + aws_wait_for_n_lifecycle_events(&test_context, AWS_MQTT5_CLET_DISCONNECTION, i + 1); } struct aws_mqtt5_packet_connect_storage clean_start_connect_storage; diff --git a/tests/v5/mqtt5_testing_utils.h b/tests/v5/mqtt5_testing_utils.h index 74cd0f8d..b1f016b8 100644 --- a/tests/v5/mqtt5_testing_utils.h +++ b/tests/v5/mqtt5_testing_utils.h @@ -228,6 +228,16 @@ int aws_mqtt5_server_send_suback_on_subscribe( struct aws_mqtt5_server_mock_connection_context *connection, void *user_data); +int aws_mqtt5_mock_server_handle_connect_honor_session_unconditional( + void *packet, + struct aws_mqtt5_server_mock_connection_context *connection, + void *user_data); + +void aws_wait_for_n_lifecycle_events( + struct aws_mqtt5_client_mock_test_fixture *test_fixture, + enum aws_mqtt5_client_lifecycle_event_type event_type, + size_t expected_event_count); + extern const struct aws_string *g_default_client_id; #define RECONNECT_TEST_MIN_BACKOFF 500 From 30ed0c36bf6ed46c9b8fdcdac6b1d066f8392576 Mon Sep 17 00:00:00 2001 From: Bret Ambrose Date: Fri, 19 Jan 2024 13:15:29 -0800 Subject: [PATCH 09/19] Move test file --- tests/CMakeLists.txt | 4 ++-- .../request_response_protocol_adapter_tests.c | 2 +- 2 files changed, 3 insertions(+), 3 deletions(-) rename tests/{ => request-response}/request_response_protocol_adapter_tests.c (99%) diff --git a/tests/CMakeLists.txt b/tests/CMakeLists.txt index 587495c4..ab5057b7 100644 --- a/tests/CMakeLists.txt +++ b/tests/CMakeLists.txt @@ -3,8 +3,8 @@ include(AwsTestHarness) include(AwsLibFuzzer) enable_testing() -file(GLOB TEST_HDRS "v3/*.h v5/*.h") -set(TEST_SRC v3/*.c v5/*.c *.c) +file(GLOB TEST_HDRS "v3/*.h" "v5/*.h" "request-response/*.h") +set(TEST_SRC "v3/*.c" "v5/*.c" "request-response/*.c" "*.c") file(GLOB TESTS ${TEST_HDRS} ${TEST_SRC}) add_test_case(mqtt_packet_puback) diff --git a/tests/request_response_protocol_adapter_tests.c b/tests/request-response/request_response_protocol_adapter_tests.c similarity index 99% rename from tests/request_response_protocol_adapter_tests.c rename to tests/request-response/request_response_protocol_adapter_tests.c index 572c8345..c2fd65fa 100644 --- a/tests/request_response_protocol_adapter_tests.c +++ b/tests/request-response/request_response_protocol_adapter_tests.c @@ -3,7 +3,7 @@ * SPDX-License-Identifier: Apache-2.0. */ -#include "v5/mqtt5_testing_utils.h" +#include "../v5/mqtt5_testing_utils.h" #include #include "aws/mqtt/private/request-response/protocol_adapter.h" From 8c23d4b5749dcc670e0294bf79d0c4171e99af8d Mon Sep 17 00:00:00 2001 From: Bret Ambrose Date: Fri, 19 Jan 2024 14:48:17 -0800 Subject: [PATCH 10/19] Commenting --- .../request-response/protocol_adapter.h | 72 ++++++++++++++++++- .../mqtt/private/request-response/weak_ref.h | 33 +++++++++ 2 files changed, 104 insertions(+), 1 deletion(-) diff --git a/include/aws/mqtt/private/request-response/protocol_adapter.h b/include/aws/mqtt/private/request-response/protocol_adapter.h index 8a61d351..4b439925 100644 --- a/include/aws/mqtt/private/request-response/protocol_adapter.h +++ b/include/aws/mqtt/private/request-response/protocol_adapter.h @@ -15,25 +15,54 @@ struct aws_allocator; struct aws_mqtt_client_connection; struct aws_mqtt5_client; +/* + * The request-response protocol adapter is a translation layer that sits between the request-response native client + * implementation and a protocol client capable of subscribing, unsubscribing, and publishing MQTT messages. + * Valid protocol clients include the CRT MQTT5 client, the CRT MQTT311 client, and an eventstream RPC connection + * that belongs to a Greengrass IPC client. Each of these protocol clients has a different (or even implicit) + * contract for carrying out pub-sub operations. The protocol adapter abstracts these details with a simple, + * minimal interface based on the requirements identigied in the request-response design documents. + */ + +/* + * Minimal MQTT subscribe options + */ struct aws_protocol_adapter_subscribe_options { struct aws_byte_cursor topic_filter; uint32_t ack_timeout_seconds; }; +/* + * Minimal MQTT unsubscribe options + */ struct aws_protocol_adapter_unsubscribe_options { struct aws_byte_cursor topic_filter; uint32_t ack_timeout_seconds; }; +/* + * Minimal MQTT publish options + */ struct aws_protocol_adapter_publish_options { struct aws_byte_cursor topic; struct aws_byte_cursor payload; + uint32_t ack_timeout_seconds; + /* + * Invoked on success/failure of the publish itself. Our implementations use QoS1 which means that success + * will be on puback receipt. + */ void (*completion_callback_fn)(bool, void *); + + /* + * User data to pass in when invoking the completion callback + */ void *user_data; - uint32_t ack_timeout_seconds; }; +/* + * Describes the type of subscription event (relative to a topic filter) + */ enum aws_protocol_adapter_subscription_event_type { AWS_PASET_SUBSCRIBE_SUCCESS, AWS_PASET_SUBSCRIBE_FAILURE, @@ -41,21 +70,36 @@ enum aws_protocol_adapter_subscription_event_type { AWS_PASET_UNSUBSCRIBE_FAILURE, }; +/* + * An event emitted by the protocol adapter when a subscribe or unsubscribe is completed by the adapted protocol + * client. + */ struct aws_protocol_adapter_subscription_event { struct aws_byte_cursor topic_filter; enum aws_protocol_adapter_subscription_event_type event_type; }; +/* + * An event emitted by the protocol adapter whenever a publish is received by the protocol client. This will + * potentially include messages that are completely unrelated to MQTT request-response. The topic is the first + * thing that should be checked for relevance. + */ struct aws_protocol_adapter_incoming_publish_event { struct aws_byte_cursor topic; struct aws_byte_cursor payload; }; +/* + * Describes the type of connection event emitted by the protocol adapter + */ enum aws_protocol_adapter_connection_event_type { AWS_PACET_OFFLINE, AWS_PACET_ONLINE, }; +/* + * An event emitted by the protocol adapter whenever the protocol client encounters a change in connectivity state. + */ struct aws_protocol_adapter_connection_event { enum aws_protocol_adapter_connection_event_type event_type; bool rejoined_session; @@ -70,12 +114,19 @@ typedef void(aws_protocol_adapter_terminate_callback_fn)(void *user_data); typedef void( aws_protocol_adapter_connection_event_fn)(struct aws_protocol_adapter_connection_event *event, void *user_data); +/* + * Set of callbacks invoked by the protocol adapter. These must all be set. + */ struct aws_mqtt_protocol_adapter_options { aws_protocol_adapter_subscription_event_fn *subscription_event_callback; aws_protocol_adapter_incoming_publish_fn *incoming_publish_callback; aws_protocol_adapter_terminate_callback_fn *terminate_callback; aws_protocol_adapter_connection_event_fn *connection_event_callback; + /* + * User data to pass into all singleton protocol adapter callbacks. Likely either the request-response client + * or the subscription manager component of the request-response client. + */ void *user_data; }; @@ -97,26 +148,45 @@ struct aws_mqtt_protocol_adapter { AWS_EXTERN_C_BEGIN +/* + * Creates a new request-response protocol adapter from an MQTT311 client + */ AWS_MQTT_API struct aws_mqtt_protocol_adapter *aws_mqtt_protocol_adapter_new_from_311( struct aws_allocator *allocator, struct aws_mqtt_protocol_adapter_options *options, struct aws_mqtt_client_connection *connection); +/* + * Creates a new request-response protocol adapter from an MQTT5 client + */ AWS_MQTT_API struct aws_mqtt_protocol_adapter *aws_mqtt_protocol_adapter_new_from_5( struct aws_allocator *allocator, struct aws_mqtt_protocol_adapter_options *options, struct aws_mqtt5_client *client); +/* + * Destroys a request-response protocol adapter. Destruction is an asynchronous process and the caller must + * wait for the termination callback to be invoked before assuming that no further callbacks will be invoked. + */ AWS_MQTT_API void aws_mqtt_protocol_adapter_delete(struct aws_mqtt_protocol_adapter *adapter); +/* + * Asks the adapted protocol client to perform an MQTT subscribe operation + */ AWS_MQTT_API int aws_mqtt_protocol_adapter_subscribe( struct aws_mqtt_protocol_adapter *adapter, struct aws_protocol_adapter_subscribe_options *options); +/* + * Asks the adapted protocol client to perform an MQTT unsubscribe operation + */ AWS_MQTT_API int aws_mqtt_protocol_adapter_unsubscribe( struct aws_mqtt_protocol_adapter *adapter, struct aws_protocol_adapter_unsubscribe_options *options); +/* + * Asks the adapted protocol client to perform an MQTT publish operation + */ AWS_MQTT_API int aws_mqtt_protocol_adapter_publish( struct aws_mqtt_protocol_adapter *adapter, struct aws_protocol_adapter_publish_options *options); diff --git a/include/aws/mqtt/private/request-response/weak_ref.h b/include/aws/mqtt/private/request-response/weak_ref.h index 0872de69..42a96764 100644 --- a/include/aws/mqtt/private/request-response/weak_ref.h +++ b/include/aws/mqtt/private/request-response/weak_ref.h @@ -10,18 +10,51 @@ #include +/* + * This is a simplification of the notion of a weak reference particular to the needs of the request-response + * MQTT service clients. This is not suitable for general use but could be extended + * for general use in the future. Until then, it stays private, here. + * + * This weak reference is a ref-counted object with an opaque value. The opaque value may be cleared or + * queried. These two operations *do not* provide any thread safety. + * + * The primary use is to allow one object to safely use asynchronous callback-driven APIs on a second object, despite + * the fact that the first object may get destroyed unpredictably. The two objects must be exclusive to a single + * event loop (because there's no thread safety or mutual exclusion on the opaque value held by the weak ref). + * + * The initial use is the request-response protocol adapter submitting operations to an MQTT client or an + * eventstream RPC connection. We use a single weak ref to the protocol adapter and zero its opaque value when + * the protocol adapter is destroyed. Operation callbacks that subsequently resolve can then short circuit and do + * nothing rather than call into garbage and crash. + */ struct aws_weak_ref; AWS_EXTERN_C_BEGIN +/* + * Creates a new weak reference to an opaque value. + */ AWS_MQTT_API struct aws_weak_ref *aws_weak_ref_new(struct aws_allocator *allocator, void *referenced); +/* + * Acquires a reference to the weak ref object. + */ AWS_MQTT_API struct aws_weak_ref *aws_weak_ref_acquire(struct aws_weak_ref *weak_ref); +/* + * Removes a reference to the weak ref object. When the last reference is removed, the weak ref object will be + * destroyed. This has no effect on the opaque value held by the weak ref. + */ AWS_MQTT_API struct aws_weak_ref *aws_weak_ref_release(struct aws_weak_ref *weak_ref); +/* + * Gets the current value of the opaque data held by the weak ref. + */ AWS_MQTT_API void *aws_weak_ref_get_reference(struct aws_weak_ref *weak_ref); +/* + * Clears the opaque data held by the weak ref. + */ AWS_MQTT_API void aws_weak_ref_zero_reference(struct aws_weak_ref *weak_ref); AWS_EXTERN_C_END From fa655f3624f80f833a7042c74517a8dd43078047 Mon Sep 17 00:00:00 2001 From: Bret Ambrose Date: Fri, 19 Jan 2024 14:51:58 -0800 Subject: [PATCH 11/19] Formatting --- source/request-response/protocol_adapter.c | 14 +++++++++----- 1 file changed, 9 insertions(+), 5 deletions(-) diff --git a/source/request-response/protocol_adapter.c b/source/request-response/protocol_adapter.c index a4dc656c..b8758723 100644 --- a/source/request-response/protocol_adapter.c +++ b/source/request-response/protocol_adapter.c @@ -310,7 +310,9 @@ static bool s_protocol_adapter_mqtt5_listener_publish_received( struct aws_mqtt_protocol_adapter_5_impl *adapter = user_data; struct aws_protocol_adapter_incoming_publish_event publish_event = { - .topic = publish->topic, .payload = publish->payload}; + .topic = publish->topic, + .payload = publish->payload, + }; (*adapter->config.incoming_publish_callback)(&publish_event, adapter->config.user_data); @@ -382,10 +384,12 @@ struct aws_mqtt_protocol_adapter *aws_mqtt_protocol_adapter_new_from_5( struct aws_mqtt5_listener_config listener_options = { .client = client, .listener_callbacks = - {.listener_publish_received_handler = s_protocol_adapter_mqtt5_listener_publish_received, - .listener_publish_received_handler_user_data = adapter, - .lifecycle_event_handler = s_protocol_adapter_mqtt5_lifecycle_event_callback, - .lifecycle_event_handler_user_data = adapter}, + { + .listener_publish_received_handler = s_protocol_adapter_mqtt5_listener_publish_received, + .listener_publish_received_handler_user_data = adapter, + .lifecycle_event_handler = s_protocol_adapter_mqtt5_lifecycle_event_callback, + .lifecycle_event_handler_user_data = adapter, + }, .termination_callback = s_protocol_adapter_mqtt5_listener_termination_callback, .termination_callback_user_data = adapter, }; From 65229df5ba62ebed4189b8cf89602d1ad47c1eb8 Mon Sep 17 00:00:00 2001 From: Bret Ambrose Date: Fri, 19 Jan 2024 14:58:54 -0800 Subject: [PATCH 12/19] Explanation why we use new approach --- include/aws/mqtt/private/request-response/weak_ref.h | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/include/aws/mqtt/private/request-response/weak_ref.h b/include/aws/mqtt/private/request-response/weak_ref.h index 42a96764..cff0a2d8 100644 --- a/include/aws/mqtt/private/request-response/weak_ref.h +++ b/include/aws/mqtt/private/request-response/weak_ref.h @@ -26,6 +26,10 @@ * eventstream RPC connection. We use a single weak ref to the protocol adapter and zero its opaque value when * the protocol adapter is destroyed. Operation callbacks that subsequently resolve can then short circuit and do * nothing rather than call into garbage and crash. + * + * We use this rather than explicitly tracking and zeroing all pending operations (like the 3-to-5 adapter does) + * because this approach is simpler and our usage does not require any of these callbacks to be invoked once the + * request-response client is destroyed. */ struct aws_weak_ref; From 5160eb4ed09e41bee2c4f552d4ba42a5d36431fb Mon Sep 17 00:00:00 2001 From: Bret Ambrose Date: Mon, 22 Jan 2024 09:20:48 -0800 Subject: [PATCH 13/19] array size macro doesn't seem to work on windows? --- tests/v5/mqtt5_client_tests.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tests/v5/mqtt5_client_tests.c b/tests/v5/mqtt5_client_tests.c index 69159cb2..b876adda 100644 --- a/tests/v5/mqtt5_client_tests.c +++ b/tests/v5/mqtt5_client_tests.c @@ -3103,7 +3103,7 @@ int aws_mqtt5_mock_server_handle_unsubscribe_unsuback_success( struct aws_mqtt5_packet_unsuback_view unsuback_view = { .packet_id = unsubscribe_view->packet_id, - .reason_code_count = AWS_ARRAY_SIZE(mqtt5_unsuback_codes), + .reason_code_count = unsubscribe_view->topic_filter_count, .reason_codes = mqtt5_unsuback_codes, }; From b1fe1f93c0e1fd46c00fbef8463c854fecd18faf Mon Sep 17 00:00:00 2001 From: Bret Ambrose Date: Wed, 24 Jan 2024 11:02:22 -0800 Subject: [PATCH 14/19] Feedback --- include/aws/mqtt/private/client_impl_shared.h | 4 +-- .../request-response/protocol_adapter.h | 6 ++-- include/aws/mqtt/v5/mqtt5_client.h | 3 ++ source/client.c | 2 +- source/request-response/protocol_adapter.c | 30 +++++++++++-------- source/v5/mqtt5_to_mqtt3_adapter.c | 2 +- .../request_response_protocol_adapter_tests.c | 2 +- 7 files changed, 29 insertions(+), 20 deletions(-) diff --git a/include/aws/mqtt/private/client_impl_shared.h b/include/aws/mqtt/private/client_impl_shared.h index 9f28894f..49961308 100644 --- a/include/aws/mqtt/private/client_impl_shared.h +++ b/include/aws/mqtt/private/client_impl_shared.h @@ -18,10 +18,10 @@ struct aws_mqtt_client_connection; enum aws_mqtt311_impl_type { /* 311 connection impl can be cast to `struct aws_mqtt_client_connection_311_impl` */ - AWS_MQTT311_IT_311_CONNECTION_IMPL, + AWS_MQTT311_IT_311_CONNECTION, /* 311 connection impl can be cast to `struct aws_mqtt_client_connection_5_impl`*/ - AWS_MQTT311_IT_5_ADAPTER_IMPL, + AWS_MQTT311_IT_5_ADAPTER, }; struct aws_mqtt_client_connection_vtable { diff --git a/include/aws/mqtt/private/request-response/protocol_adapter.h b/include/aws/mqtt/private/request-response/protocol_adapter.h index 4b439925..6f596601 100644 --- a/include/aws/mqtt/private/request-response/protocol_adapter.h +++ b/include/aws/mqtt/private/request-response/protocol_adapter.h @@ -21,7 +21,7 @@ struct aws_mqtt5_client; * Valid protocol clients include the CRT MQTT5 client, the CRT MQTT311 client, and an eventstream RPC connection * that belongs to a Greengrass IPC client. Each of these protocol clients has a different (or even implicit) * contract for carrying out pub-sub operations. The protocol adapter abstracts these details with a simple, - * minimal interface based on the requirements identigied in the request-response design documents. + * minimal interface based on the requirements identified in the request-response design documents. */ /* @@ -132,7 +132,7 @@ struct aws_mqtt_protocol_adapter_options { struct aws_mqtt_protocol_adapter_vtable { - void (*aws_mqtt_protocol_adapter_delete_fn)(void *); + void (*aws_mqtt_protocol_adapter_destroy_fn)(void *); int (*aws_mqtt_protocol_adapter_subscribe_fn)(void *, struct aws_protocol_adapter_subscribe_options *); @@ -168,7 +168,7 @@ AWS_MQTT_API struct aws_mqtt_protocol_adapter *aws_mqtt_protocol_adapter_new_fro * Destroys a request-response protocol adapter. Destruction is an asynchronous process and the caller must * wait for the termination callback to be invoked before assuming that no further callbacks will be invoked. */ -AWS_MQTT_API void aws_mqtt_protocol_adapter_delete(struct aws_mqtt_protocol_adapter *adapter); +AWS_MQTT_API void aws_mqtt_protocol_adapter_destroy(struct aws_mqtt_protocol_adapter *adapter); /* * Asks the adapted protocol client to perform an MQTT subscribe operation diff --git a/include/aws/mqtt/v5/mqtt5_client.h b/include/aws/mqtt/v5/mqtt5_client.h index d04d2981..5cac371c 100644 --- a/include/aws/mqtt/v5/mqtt5_client.h +++ b/include/aws/mqtt/v5/mqtt5_client.h @@ -341,6 +341,7 @@ struct aws_mqtt5_publish_completion_options { aws_mqtt5_publish_completion_fn *completion_callback; void *completion_user_data; + /** Overrides the client's ack timeout with this value, for this operation only */ uint32_t ack_timeout_seconds_override; }; @@ -351,6 +352,7 @@ struct aws_mqtt5_subscribe_completion_options { aws_mqtt5_subscribe_completion_fn *completion_callback; void *completion_user_data; + /** Overrides the client's ack timeout with this value, for this operation only */ uint32_t ack_timeout_seconds_override; }; @@ -361,6 +363,7 @@ struct aws_mqtt5_unsubscribe_completion_options { aws_mqtt5_unsubscribe_completion_fn *completion_callback; void *completion_user_data; + /** Overrides the client's ack timeout with this value, for this operation only */ uint32_t ack_timeout_seconds_override; }; diff --git a/source/client.c b/source/client.c index 42ab634c..631a26c0 100644 --- a/source/client.c +++ b/source/client.c @@ -3223,7 +3223,7 @@ static void s_aws_mqtt_client_connection_311_release(void *impl) { enum aws_mqtt311_impl_type s_aws_mqtt_client_connection_3_get_impl(void *impl) { (void)impl; - return AWS_MQTT311_IT_311_CONNECTION_IMPL; + return AWS_MQTT311_IT_311_CONNECTION; } static struct aws_mqtt_client_connection_vtable s_aws_mqtt_client_connection_311_vtable = { diff --git a/source/request-response/protocol_adapter.c b/source/request-response/protocol_adapter.c index b8758723..652ba5b2 100644 --- a/source/request-response/protocol_adapter.c +++ b/source/request-response/protocol_adapter.c @@ -58,7 +58,7 @@ struct aws_mqtt_protocol_adapter_5_impl { struct aws_mqtt5_listener *listener; }; -static void s_aws_mqtt_protocol_adapter_5_delete(void *impl) { +static void s_aws_mqtt_protocol_adapter_5_destroy(void *impl) { struct aws_mqtt_protocol_adapter_5_impl *adapter = impl; // all the real cleanup is done in the listener termination callback @@ -87,7 +87,7 @@ static struct aws_mqtt_protocol_adapter_5_subscription_op_data *s_aws_mqtt_proto return subscribe_data; } -static void s_aws_mqtt_protocol_adapter_5_subscription_op_data_delete( +static void s_aws_mqtt_protocol_adapter_5_subscription_op_data_destroy( struct aws_mqtt_protocol_adapter_5_subscription_op_data *subscribe_data) { aws_weak_ref_release(subscribe_data->callback_ref); aws_byte_buf_clean_up(&subscribe_data->topic_filter); @@ -120,7 +120,7 @@ static void s_protocol_adapter_5_subscribe_completion( done: - s_aws_mqtt_protocol_adapter_5_subscription_op_data_delete(subscribe_data); + s_aws_mqtt_protocol_adapter_5_subscription_op_data_destroy(subscribe_data); } int s_aws_mqtt_protocol_adapter_5_subscribe(void *impl, struct aws_protocol_adapter_subscribe_options *options) { @@ -154,7 +154,7 @@ int s_aws_mqtt_protocol_adapter_5_subscribe(void *impl, struct aws_protocol_adap error: - s_aws_mqtt_protocol_adapter_5_subscription_op_data_delete(subscribe_data); + s_aws_mqtt_protocol_adapter_5_subscription_op_data_destroy(subscribe_data); return AWS_OP_ERR; } @@ -184,7 +184,7 @@ static void s_protocol_adapter_5_unsubscribe_completion( done: - s_aws_mqtt_protocol_adapter_5_subscription_op_data_delete(unsubscribe_data); + s_aws_mqtt_protocol_adapter_5_subscription_op_data_destroy(unsubscribe_data); } int s_aws_mqtt_protocol_adapter_5_unsubscribe(void *impl, struct aws_protocol_adapter_unsubscribe_options *options) { @@ -213,7 +213,7 @@ int s_aws_mqtt_protocol_adapter_5_unsubscribe(void *impl, struct aws_protocol_ad error: - s_aws_mqtt_protocol_adapter_5_subscription_op_data_delete(unsubscribe_data); + s_aws_mqtt_protocol_adapter_5_subscription_op_data_destroy(unsubscribe_data); return AWS_OP_ERR; } @@ -243,7 +243,7 @@ static struct aws_mqtt_protocol_adapter_5_publish_op_data *s_aws_mqtt_protocol_a return publish_data; } -static void s_aws_mqtt_protocol_adapter_5_publish_op_data_delete( +static void s_aws_mqtt_protocol_adapter_5_publish_op_data_destroy( struct aws_mqtt_protocol_adapter_5_publish_op_data *publish_data) { aws_weak_ref_release(publish_data->callback_ref); @@ -274,7 +274,7 @@ static void s_protocol_adapter_5_publish_completion( done: - s_aws_mqtt_protocol_adapter_5_publish_op_data_delete(publish_data); + s_aws_mqtt_protocol_adapter_5_publish_op_data_destroy(publish_data); } int s_aws_mqtt_protocol_adapter_5_publish(void *impl, struct aws_protocol_adapter_publish_options *options) { @@ -299,7 +299,7 @@ int s_aws_mqtt_protocol_adapter_5_publish(void *impl, struct aws_protocol_adapte error: - s_aws_mqtt_protocol_adapter_5_publish_op_data_delete(publish_data); + s_aws_mqtt_protocol_adapter_5_publish_op_data_destroy(publish_data); return AWS_OP_ERR; } @@ -360,7 +360,7 @@ static void s_protocol_adapter_mqtt5_listener_termination_callback(void *user_da } static struct aws_mqtt_protocol_adapter_vtable s_protocol_adapter_mqtt5_vtable = { - .aws_mqtt_protocol_adapter_delete_fn = s_aws_mqtt_protocol_adapter_5_delete, + .aws_mqtt_protocol_adapter_destroy_fn = s_aws_mqtt_protocol_adapter_5_destroy, .aws_mqtt_protocol_adapter_subscribe_fn = s_aws_mqtt_protocol_adapter_5_subscribe, .aws_mqtt_protocol_adapter_unsubscribe_fn = s_aws_mqtt_protocol_adapter_5_unsubscribe, .aws_mqtt_protocol_adapter_publish_fn = s_aws_mqtt_protocol_adapter_5_publish, @@ -370,6 +370,12 @@ struct aws_mqtt_protocol_adapter *aws_mqtt_protocol_adapter_new_from_5( struct aws_allocator *allocator, struct aws_mqtt_protocol_adapter_options *options, struct aws_mqtt5_client *client) { + + if (options == NULL || client == NULL) { + aws_raise_error(AWS_ERROR_INVALID_ARGUMENT); + return NULL; + } + struct aws_mqtt_protocol_adapter_5_impl *adapter = aws_mem_calloc(allocator, 1, sizeof(struct aws_mqtt_protocol_adapter_5_impl)); @@ -399,8 +405,8 @@ struct aws_mqtt_protocol_adapter *aws_mqtt_protocol_adapter_new_from_5( return &adapter->base; } -void aws_mqtt_protocol_adapter_delete(struct aws_mqtt_protocol_adapter *adapter) { - (*adapter->vtable->aws_mqtt_protocol_adapter_delete_fn)(adapter->impl); +void aws_mqtt_protocol_adapter_destroy(struct aws_mqtt_protocol_adapter *adapter) { + (*adapter->vtable->aws_mqtt_protocol_adapter_destroy_fn)(adapter->impl); } int aws_mqtt_protocol_adapter_subscribe( diff --git a/source/v5/mqtt5_to_mqtt3_adapter.c b/source/v5/mqtt5_to_mqtt3_adapter.c index fb39e444..2cb0451d 100644 --- a/source/v5/mqtt5_to_mqtt3_adapter.c +++ b/source/v5/mqtt5_to_mqtt3_adapter.c @@ -2857,7 +2857,7 @@ static uint16_t s_aws_mqtt_5_resubscribe_existing_topics( enum aws_mqtt311_impl_type s_aws_mqtt_client_connection_5_get_impl(void *impl) { (void)impl; - return AWS_MQTT311_IT_5_ADAPTER_IMPL; + return AWS_MQTT311_IT_5_ADAPTER; } static struct aws_mqtt_client_connection_vtable s_aws_mqtt_client_connection_5_vtable = { diff --git a/tests/request-response/request_response_protocol_adapter_tests.c b/tests/request-response/request_response_protocol_adapter_tests.c index c2fd65fa..6bca4aaa 100644 --- a/tests/request-response/request_response_protocol_adapter_tests.c +++ b/tests/request-response/request_response_protocol_adapter_tests.c @@ -190,7 +190,7 @@ static bool s_is_adapter_terminated(void *context) { static void s_aws_request_response_mqtt5_adapter_test_fixture_destroy_adapters( struct aws_request_response_mqtt5_adapter_test_fixture *fixture) { if (fixture->protocol_adapter != NULL) { - aws_mqtt_protocol_adapter_delete(fixture->protocol_adapter); + aws_mqtt_protocol_adapter_destroy(fixture->protocol_adapter); aws_mutex_lock(&fixture->lock); aws_condition_variable_wait_pred(&fixture->signal, &fixture->lock, s_is_adapter_terminated, fixture); From e5f93e1e3cfa81e67c8bc85fbec86d0145bb6bc4 Mon Sep 17 00:00:00 2001 From: Bret Ambrose Date: Wed, 24 Jan 2024 15:24:15 -0800 Subject: [PATCH 15/19] Convert to session event --- .../request-response/protocol_adapter.h | 20 +--- source/request-response/protocol_adapter.c | 14 +-- tests/CMakeLists.txt | 3 +- .../request_response_protocol_adapter_tests.c | 106 ++++++++---------- 4 files changed, 59 insertions(+), 84 deletions(-) diff --git a/include/aws/mqtt/private/request-response/protocol_adapter.h b/include/aws/mqtt/private/request-response/protocol_adapter.h index 6f596601..c70a2b99 100644 --- a/include/aws/mqtt/private/request-response/protocol_adapter.h +++ b/include/aws/mqtt/private/request-response/protocol_adapter.h @@ -90,19 +90,10 @@ struct aws_protocol_adapter_incoming_publish_event { }; /* - * Describes the type of connection event emitted by the protocol adapter + * An event emitted by the protocol adapter whenever the protocol client successfully reconnects to the broker. */ -enum aws_protocol_adapter_connection_event_type { - AWS_PACET_OFFLINE, - AWS_PACET_ONLINE, -}; - -/* - * An event emitted by the protocol adapter whenever the protocol client encounters a change in connectivity state. - */ -struct aws_protocol_adapter_connection_event { - enum aws_protocol_adapter_connection_event_type event_type; - bool rejoined_session; +struct aws_protocol_adapter_session_event { + bool joined_session; }; typedef void( @@ -111,8 +102,7 @@ typedef void(aws_protocol_adapter_incoming_publish_fn)( struct aws_protocol_adapter_incoming_publish_event *publish, void *user_data); typedef void(aws_protocol_adapter_terminate_callback_fn)(void *user_data); -typedef void( - aws_protocol_adapter_connection_event_fn)(struct aws_protocol_adapter_connection_event *event, void *user_data); +typedef void(aws_protocol_adapter_session_event_fn)(struct aws_protocol_adapter_session_event *event, void *user_data); /* * Set of callbacks invoked by the protocol adapter. These must all be set. @@ -121,7 +111,7 @@ struct aws_mqtt_protocol_adapter_options { aws_protocol_adapter_subscription_event_fn *subscription_event_callback; aws_protocol_adapter_incoming_publish_fn *incoming_publish_callback; aws_protocol_adapter_terminate_callback_fn *terminate_callback; - aws_protocol_adapter_connection_event_fn *connection_event_callback; + aws_protocol_adapter_session_event_fn *session_event_callback; /* * User data to pass into all singleton protocol adapter callbacks. Likely either the request-response client diff --git a/source/request-response/protocol_adapter.c b/source/request-response/protocol_adapter.c index 652ba5b2..a277f9ce 100644 --- a/source/request-response/protocol_adapter.c +++ b/source/request-response/protocol_adapter.c @@ -322,21 +322,15 @@ static bool s_protocol_adapter_mqtt5_listener_publish_received( static void s_protocol_adapter_mqtt5_lifecycle_event_callback(const struct aws_mqtt5_client_lifecycle_event *event) { struct aws_mqtt_protocol_adapter_5_impl *adapter = event->user_data; - if (event->event_type != AWS_MQTT5_CLET_CONNECTION_SUCCESS && event->event_type != AWS_MQTT5_CLET_DISCONNECTION) { + if (event->event_type != AWS_MQTT5_CLET_CONNECTION_SUCCESS) { return; } - bool is_connection_success = event->event_type == AWS_MQTT5_CLET_CONNECTION_SUCCESS; - - struct aws_protocol_adapter_connection_event connection_event = { - .event_type = is_connection_success ? AWS_PACET_ONLINE : AWS_PACET_OFFLINE, + struct aws_protocol_adapter_session_event session_event = { + .joined_session = event->settings->rejoined_session, }; - if (is_connection_success) { - connection_event.rejoined_session = event->settings->rejoined_session; - } - - (*adapter->config.connection_event_callback)(&connection_event, adapter->config.user_data); + (*adapter->config.session_event_callback)(&session_event, adapter->config.user_data); } static void s_protocol_adapter_mqtt5_listener_termination_callback(void *user_data) { diff --git a/tests/CMakeLists.txt b/tests/CMakeLists.txt index ab5057b7..d93b7427 100644 --- a/tests/CMakeLists.txt +++ b/tests/CMakeLists.txt @@ -454,7 +454,8 @@ add_test_case(request_response_mqtt5_protocol_adapter_publish_success) add_test_case(request_response_mqtt5_protocol_adapter_publish_failure_error_code) add_test_case(request_response_mqtt5_protocol_adapter_publish_failure_reason_code) add_test_case(request_response_mqtt5_protocol_adapter_publish_failure_timeout) -add_test_case(request_response_mqtt5_protocol_adapter_connection_event_sequence) +add_test_case(request_response_mqtt5_protocol_adapter_session_event_no_rejoin) +add_test_case(request_response_mqtt5_protocol_adapter_session_event_rejoin) add_test_case(request_response_mqtt5_protocol_adapter_incoming_publish) add_test_case(request_response_mqtt5_protocol_adapter_shutdown_while_pending) diff --git a/tests/request-response/request_response_protocol_adapter_tests.c b/tests/request-response/request_response_protocol_adapter_tests.c index 6bca4aaa..586c9c00 100644 --- a/tests/request-response/request_response_protocol_adapter_tests.c +++ b/tests/request-response/request_response_protocol_adapter_tests.c @@ -31,11 +31,6 @@ static void s_request_response_protocol_adapter_incoming_publish_event_record_cl aws_byte_buf_clean_up(&record->payload); } -struct request_response_protocol_adapter_connection_event_record { - enum aws_protocol_adapter_connection_event_type event_type; - bool rejoined_session; -}; - struct request_response_protocol_adapter_subscription_event_record { enum aws_protocol_adapter_subscription_event_type event_type; struct aws_byte_buf topic_filter; @@ -61,7 +56,7 @@ struct aws_request_response_mqtt5_adapter_test_fixture { struct aws_mqtt_protocol_adapter *protocol_adapter; struct aws_array_list incoming_publish_events; - struct aws_array_list connection_events; + struct aws_array_list session_events; struct aws_array_list subscription_events; struct aws_array_list publish_results; @@ -111,16 +106,13 @@ static void s_rr_mqtt5_protocol_adapter_test_on_terminate_callback(void *user_da aws_condition_variable_notify_all(&fixture->signal); } -static void s_rr_mqtt5_protocol_adapter_test_on_connection_event( - struct aws_protocol_adapter_connection_event *event, +static void s_rr_mqtt5_protocol_adapter_test_on_session_event( + struct aws_protocol_adapter_session_event *event, void *user_data) { struct aws_request_response_mqtt5_adapter_test_fixture *fixture = user_data; - struct request_response_protocol_adapter_connection_event_record record = { - .event_type = event->event_type, .rejoined_session = event->rejoined_session}; - aws_mutex_lock(&fixture->lock); - aws_array_list_push_back(&fixture->connection_events, &record); + aws_array_list_push_back(&fixture->session_events, event); aws_mutex_unlock(&fixture->lock); aws_condition_variable_notify_all(&fixture->signal); } @@ -151,7 +143,7 @@ static int s_aws_request_response_mqtt5_adapter_test_fixture_init( .subscription_event_callback = s_rr_mqtt5_protocol_adapter_test_on_subscription_event, .incoming_publish_callback = s_rr_mqtt5_protocol_adapter_test_on_incoming_publish, .terminate_callback = s_rr_mqtt5_protocol_adapter_test_on_terminate_callback, - .connection_event_callback = s_rr_mqtt5_protocol_adapter_test_on_connection_event, + .session_event_callback = s_rr_mqtt5_protocol_adapter_test_on_session_event, .user_data = fixture}; fixture->protocol_adapter = @@ -164,10 +156,7 @@ static int s_aws_request_response_mqtt5_adapter_test_fixture_init( 10, sizeof(struct request_response_protocol_adapter_incoming_publish_event_record)); aws_array_list_init_dynamic( - &fixture->connection_events, - allocator, - 10, - sizeof(struct request_response_protocol_adapter_connection_event_record)); + &fixture->session_events, allocator, 10, sizeof(struct aws_protocol_adapter_session_event)); aws_array_list_init_dynamic( &fixture->subscription_events, allocator, @@ -220,7 +209,7 @@ static void s_aws_request_response_mqtt5_adapter_test_fixture_clean_up( } aws_array_list_clean_up(&fixture->incoming_publish_events); - aws_array_list_clean_up(&fixture->connection_events); + aws_array_list_clean_up(&fixture->session_events); aws_array_list_clean_up(&fixture->publish_results); aws_mutex_clean_up(&fixture->lock); @@ -272,24 +261,23 @@ static void s_wait_for_subscription_events_contains( aws_mutex_unlock(&fixture->lock); } -struct test_connection_event_wait_context { - struct request_response_protocol_adapter_connection_event_record *expected_event; +struct test_session_event_wait_context { + struct aws_protocol_adapter_session_event *expected_event; size_t expected_count; struct aws_request_response_mqtt5_adapter_test_fixture *fixture; }; -static bool s_do_connection_events_contain(void *context) { - struct test_connection_event_wait_context *wait_context = context; +static bool s_do_session_events_contain(void *context) { + struct test_session_event_wait_context *wait_context = context; size_t found = 0; - size_t num_events = aws_array_list_length(&wait_context->fixture->connection_events); + size_t num_events = aws_array_list_length(&wait_context->fixture->session_events); for (size_t i = 0; i < num_events; ++i) { - struct request_response_protocol_adapter_connection_event_record record; - aws_array_list_get_at(&wait_context->fixture->connection_events, &record, i); + struct aws_protocol_adapter_session_event record; + aws_array_list_get_at(&wait_context->fixture->session_events, &record, i); - if (record.event_type == wait_context->expected_event->event_type && - record.rejoined_session == wait_context->expected_event->rejoined_session) { + if (record.joined_session == wait_context->expected_event->joined_session) { ++found; } } @@ -297,19 +285,19 @@ static bool s_do_connection_events_contain(void *context) { return found >= wait_context->expected_count; } -static void s_wait_for_connection_events_contains( +static void s_wait_for_session_events_contains( struct aws_request_response_mqtt5_adapter_test_fixture *fixture, - struct request_response_protocol_adapter_connection_event_record *expected_event, + struct aws_protocol_adapter_session_event *expected_event, size_t expected_count) { - struct test_connection_event_wait_context context = { + struct test_session_event_wait_context context = { .expected_event = expected_event, .expected_count = expected_count, .fixture = fixture, }; aws_mutex_lock(&fixture->lock); - aws_condition_variable_wait_pred(&fixture->signal, &fixture->lock, s_do_connection_events_contain, &context); + aws_condition_variable_wait_pred(&fixture->signal, &fixture->lock, s_do_session_events_contain, &context); aws_mutex_unlock(&fixture->lock); } @@ -818,11 +806,9 @@ AWS_TEST_CASE( request_response_mqtt5_protocol_adapter_publish_failure_error_code, s_request_response_mqtt5_protocol_adapter_publish_failure_error_code_fn) -static int s_request_response_mqtt5_protocol_adapter_connection_event_sequence_fn( +static int s_do_request_response_mqtt5_protocol_adapter_session_event_test( struct aws_allocator *allocator, - void *ctx) { - (void)ctx; - + bool rejoin_session) { aws_mqtt_library_init(allocator); struct mqtt5_client_test_options test_options; @@ -830,7 +816,7 @@ static int s_request_response_mqtt5_protocol_adapter_connection_event_sequence_f test_options.server_function_table.packet_handlers[AWS_MQTT5_PT_CONNECT] = aws_mqtt5_mock_server_handle_connect_honor_session_unconditional; - test_options.client_options.session_behavior = AWS_MQTT5_CSBT_REJOIN_POST_SUCCESS; + test_options.client_options.session_behavior = rejoin_session ? AWS_MQTT5_CSBT_REJOIN_ALWAYS : AWS_MQTT5_CSBT_CLEAN; struct aws_mqtt5_client_mqtt5_mock_test_fixture_options mqtt5_test_fixture_options = { .client_options = &test_options.client_options, @@ -843,31 +829,15 @@ static int s_request_response_mqtt5_protocol_adapter_connection_event_sequence_f struct aws_mqtt5_client *client = fixture.mqtt5_fixture.client; - struct request_response_protocol_adapter_connection_event_record online_record1 = { - .event_type = AWS_PACET_ONLINE, - .rejoined_session = false, - }; - - ASSERT_SUCCESS(aws_mqtt5_client_start(client)); - s_wait_for_connection_events_contains(&fixture, &online_record1, 1); - - struct request_response_protocol_adapter_connection_event_record offline_record = { - .event_type = AWS_PACET_OFFLINE, - }; - - ASSERT_SUCCESS(aws_mqtt5_client_stop(client, NULL, NULL)); - s_wait_for_connection_events_contains(&fixture, &offline_record, 1); - - struct request_response_protocol_adapter_connection_event_record online_record2 = { - .event_type = AWS_PACET_ONLINE, - .rejoined_session = true, + struct aws_protocol_adapter_session_event expected_session_record = { + .joined_session = rejoin_session, }; ASSERT_SUCCESS(aws_mqtt5_client_start(client)); - s_wait_for_connection_events_contains(&fixture, &online_record2, 1); + s_wait_for_session_events_contains(&fixture, &expected_session_record, 1); ASSERT_SUCCESS(aws_mqtt5_client_stop(client, NULL, NULL)); - s_wait_for_connection_events_contains(&fixture, &offline_record, 2); + aws_wait_for_stopped_lifecycle_event(&fixture.mqtt5_fixture); s_aws_request_response_mqtt5_adapter_test_fixture_clean_up(&fixture); @@ -876,9 +846,29 @@ static int s_request_response_mqtt5_protocol_adapter_connection_event_sequence_f return AWS_OP_SUCCESS; } +static int s_request_response_mqtt5_protocol_adapter_session_event_no_rejoin_fn( + struct aws_allocator *allocator, + void *ctx) { + (void)ctx; + + return s_do_request_response_mqtt5_protocol_adapter_session_event_test(allocator, false); +} + +AWS_TEST_CASE( + request_response_mqtt5_protocol_adapter_session_event_no_rejoin, + s_request_response_mqtt5_protocol_adapter_session_event_no_rejoin_fn) + +static int s_request_response_mqtt5_protocol_adapter_session_event_rejoin_fn( + struct aws_allocator *allocator, + void *ctx) { + (void)ctx; + + return s_do_request_response_mqtt5_protocol_adapter_session_event_test(allocator, true); +} + AWS_TEST_CASE( - request_response_mqtt5_protocol_adapter_connection_event_sequence, - s_request_response_mqtt5_protocol_adapter_connection_event_sequence_fn) + request_response_mqtt5_protocol_adapter_session_event_rejoin, + s_request_response_mqtt5_protocol_adapter_session_event_rejoin_fn) static int s_request_response_mqtt5_protocol_adapter_incoming_publish_fn(struct aws_allocator *allocator, void *ctx) { (void)ctx; From a54824a1813ab678af6c5eb6fef77991063ef436 Mon Sep 17 00:00:00 2001 From: Bret Ambrose Date: Thu, 25 Jan 2024 09:07:26 -0800 Subject: [PATCH 16/19] Stop talking to myself --- source/request-response/protocol_adapter.c | 18 ++++++++---------- 1 file changed, 8 insertions(+), 10 deletions(-) diff --git a/source/request-response/protocol_adapter.c b/source/request-response/protocol_adapter.c index a277f9ce..da604023 100644 --- a/source/request-response/protocol_adapter.c +++ b/source/request-response/protocol_adapter.c @@ -13,24 +13,22 @@ #include /* - * New API contract + * Basic API contract * * Invariant 1: Subscribe is only called from the RR subscription manager when going from 0 to 1 pending operations * Invariant 2: Unsubscribe is only called from the RR subscription manager when there are 0 pending operations, not * necessarily on the exact transition to zero though. * + * Additional Notes + * * Entries are not tracked with the exception of eventstream impl which needs the stream handles to close. - * A subscribe failure should not trigger an unsubscribe, only notify the status callback. - * Subscription event callback should be {subscribe_success, subscribe_failure, unsubscribe_success, - * unsubscribe_failure}. The sub manager is responsible for calling Unsubscribe on all its entries when shutting down - * (before releasing hold of the adapter). * - * How do we know not to retry unsubscribe failures because a subscribe came in? Well, we don't retry failures; let - * the manager make that decision. No retry when the weak ref is zeroed either. The potential for things to go wrong - * is worse than the potential of a subscription "leaking." + * A subscribe failure does not trigger an unsubscribe, a status event. + * + * The sub manager is responsible for calling Unsubscribe on all its entries when shutting down + * (before releasing hold of the adapter). * - * On subscribe failures with zeroed weak ref, trust that an Unsubscribe was sent that will resolve later and let it - * decide what to do. + * Retries, when appropriate, are the responsibility of the caller. */ struct aws_mqtt_protocol_adapter *aws_mqtt_protocol_adapter_new_from_311( From abbcdc1688f4f3af72003218433959b63178887a Mon Sep 17 00:00:00 2001 From: Bret Ambrose Date: Fri, 26 Jan 2024 07:26:48 -0800 Subject: [PATCH 17/19] Sync point --- include/aws/mqtt/private/request-response/protocol_adapter.h | 1 + source/request-response/protocol_adapter.c | 2 ++ 2 files changed, 3 insertions(+) diff --git a/include/aws/mqtt/private/request-response/protocol_adapter.h b/include/aws/mqtt/private/request-response/protocol_adapter.h index c70a2b99..82f10c0b 100644 --- a/include/aws/mqtt/private/request-response/protocol_adapter.h +++ b/include/aws/mqtt/private/request-response/protocol_adapter.h @@ -77,6 +77,7 @@ enum aws_protocol_adapter_subscription_event_type { struct aws_protocol_adapter_subscription_event { struct aws_byte_cursor topic_filter; enum aws_protocol_adapter_subscription_event_type event_type; + int error_code; }; /* diff --git a/source/request-response/protocol_adapter.c b/source/request-response/protocol_adapter.c index da604023..5b3b41f9 100644 --- a/source/request-response/protocol_adapter.c +++ b/source/request-response/protocol_adapter.c @@ -112,6 +112,7 @@ static void s_protocol_adapter_5_subscribe_completion( struct aws_protocol_adapter_subscription_event subscribe_event = { .topic_filter = aws_byte_cursor_from_buf(&subscribe_data->topic_filter), .event_type = success ? AWS_PASET_SUBSCRIBE_SUCCESS : AWS_PASET_SUBSCRIBE_FAILURE, + .error_code = error_code, }; (*adapter->config.subscription_event_callback)(&subscribe_event, adapter->config.user_data); @@ -176,6 +177,7 @@ static void s_protocol_adapter_5_unsubscribe_completion( struct aws_protocol_adapter_subscription_event unsubscribe_event = { .topic_filter = aws_byte_cursor_from_buf(&unsubscribe_data->topic_filter), .event_type = success ? AWS_PASET_UNSUBSCRIBE_SUCCESS : AWS_PASET_UNSUBSCRIBE_FAILURE, + .error_code = error_code, }; (*adapter->config.subscription_event_callback)(&unsubscribe_event, adapter->config.user_data); From 426878f11abc8df56ee1b0e0a35fbea8a16e95f1 Mon Sep 17 00:00:00 2001 From: Bret Ambrose Date: Fri, 26 Jan 2024 10:33:02 -0800 Subject: [PATCH 18/19] success/failure -> error code --- include/aws/mqtt/mqtt.h | 1 + .../request-response/protocol_adapter.h | 8 +- source/mqtt.c | 3 + source/request-response/protocol_adapter.c | 27 +++-- .../request_response_protocol_adapter_tests.c | 101 ++++++++++++------ 5 files changed, 92 insertions(+), 48 deletions(-) diff --git a/include/aws/mqtt/mqtt.h b/include/aws/mqtt/mqtt.h index 974c4576..8e7cd0ef 100644 --- a/include/aws/mqtt/mqtt.h +++ b/include/aws/mqtt/mqtt.h @@ -81,6 +81,7 @@ enum aws_mqtt_error { AWS_ERROR_MQTT_CONNECTION_RESET_FOR_ADAPTER_CONNECT, AWS_ERROR_MQTT_CONNECTION_RESUBSCRIBE_NO_TOPICS, AWS_ERROR_MQTT_CONNECTION_SUBSCRIBE_FAILURE, + AWS_ERROR_MQTT_PROTOCOL_ADAPTER_FAILING_REASON_CODE, AWS_ERROR_END_MQTT_RANGE = AWS_ERROR_ENUM_END_RANGE(AWS_C_MQTT_PACKAGE_ID), }; diff --git a/include/aws/mqtt/private/request-response/protocol_adapter.h b/include/aws/mqtt/private/request-response/protocol_adapter.h index 82f10c0b..f09d6135 100644 --- a/include/aws/mqtt/private/request-response/protocol_adapter.h +++ b/include/aws/mqtt/private/request-response/protocol_adapter.h @@ -52,7 +52,7 @@ struct aws_protocol_adapter_publish_options { * Invoked on success/failure of the publish itself. Our implementations use QoS1 which means that success * will be on puback receipt. */ - void (*completion_callback_fn)(bool, void *); + void (*completion_callback_fn)(int, void *); /* * User data to pass in when invoking the completion callback @@ -64,10 +64,8 @@ struct aws_protocol_adapter_publish_options { * Describes the type of subscription event (relative to a topic filter) */ enum aws_protocol_adapter_subscription_event_type { - AWS_PASET_SUBSCRIBE_SUCCESS, - AWS_PASET_SUBSCRIBE_FAILURE, - AWS_PASET_UNSUBSCRIBE_SUCCESS, - AWS_PASET_UNSUBSCRIBE_FAILURE, + AWS_PASET_SUBSCRIBE, + AWS_PASET_UNSUBSCRIBE, }; /* diff --git a/source/mqtt.c b/source/mqtt.c index c87ec0cc..fd16340a 100644 --- a/source/mqtt.c +++ b/source/mqtt.c @@ -233,6 +233,9 @@ bool aws_mqtt_is_valid_topic_filter(const struct aws_byte_cursor *topic_filter) AWS_DEFINE_ERROR_INFO_MQTT( AWS_ERROR_MQTT_CONNECTION_SUBSCRIBE_FAILURE, "MQTT subscribe operation failed"), + AWS_DEFINE_ERROR_INFO_MQTT( + AWS_ERROR_MQTT_PROTOCOL_ADAPTER_FAILING_REASON_CODE, + "MQTT operation returned a failing reason code"), }; /* clang-format on */ #undef AWS_DEFINE_ERROR_INFO_MQTT diff --git a/source/request-response/protocol_adapter.c b/source/request-response/protocol_adapter.c index 5b3b41f9..68cc8667 100644 --- a/source/request-response/protocol_adapter.c +++ b/source/request-response/protocol_adapter.c @@ -106,12 +106,15 @@ static void s_protocol_adapter_5_subscribe_completion( goto done; } - bool success = error_code == AWS_ERROR_SUCCESS && suback != NULL && suback->reason_code_count == 1 && - suback->reason_codes[0] <= AWS_MQTT5_SARC_GRANTED_QOS_2; + if (error_code == AWS_ERROR_SUCCESS) { + if (suback == NULL || suback->reason_code_count != 1 || suback->reason_codes[0] >= 128) { + error_code = AWS_ERROR_MQTT_PROTOCOL_ADAPTER_FAILING_REASON_CODE; + } + } struct aws_protocol_adapter_subscription_event subscribe_event = { .topic_filter = aws_byte_cursor_from_buf(&subscribe_data->topic_filter), - .event_type = success ? AWS_PASET_SUBSCRIBE_SUCCESS : AWS_PASET_SUBSCRIBE_FAILURE, + .event_type = AWS_PASET_SUBSCRIBE, .error_code = error_code, }; @@ -171,12 +174,15 @@ static void s_protocol_adapter_5_unsubscribe_completion( goto done; } - bool success = error_code == AWS_ERROR_SUCCESS && unsuback != NULL && unsuback->reason_code_count == 1 && - unsuback->reason_codes[0] < 128; + if (error_code == AWS_ERROR_SUCCESS) { + if (unsuback == NULL || unsuback->reason_code_count != 1 || unsuback->reason_codes[0] >= 128) { + error_code = AWS_ERROR_MQTT_PROTOCOL_ADAPTER_FAILING_REASON_CODE; + } + } struct aws_protocol_adapter_subscription_event unsubscribe_event = { .topic_filter = aws_byte_cursor_from_buf(&unsubscribe_data->topic_filter), - .event_type = success ? AWS_PASET_UNSUBSCRIBE_SUCCESS : AWS_PASET_UNSUBSCRIBE_FAILURE, + .event_type = AWS_PASET_UNSUBSCRIBE, .error_code = error_code, }; @@ -224,7 +230,7 @@ struct aws_mqtt_protocol_adapter_5_publish_op_data { struct aws_allocator *allocator; struct aws_weak_ref *callback_ref; - void (*completion_callback_fn)(bool, void *); + void (*completion_callback_fn)(int, void *); void *user_data; }; @@ -262,15 +268,14 @@ static void s_protocol_adapter_5_publish_completion( goto done; } - bool success = false; if (error_code == AWS_ERROR_SUCCESS && packet_type == AWS_MQTT5_PT_PUBACK) { const struct aws_mqtt5_packet_puback_view *puback = packet; - if (puback->reason_code < 128) { - success = true; + if (puback->reason_code >= 128) { + error_code = AWS_ERROR_MQTT_PROTOCOL_ADAPTER_FAILING_REASON_CODE; } } - (*publish_data->completion_callback_fn)(success, publish_data->user_data); + (*publish_data->completion_callback_fn)(error_code, publish_data->user_data); done: diff --git a/tests/request-response/request_response_protocol_adapter_tests.c b/tests/request-response/request_response_protocol_adapter_tests.c index 586c9c00..2034f460 100644 --- a/tests/request-response/request_response_protocol_adapter_tests.c +++ b/tests/request-response/request_response_protocol_adapter_tests.c @@ -34,13 +34,20 @@ static void s_request_response_protocol_adapter_incoming_publish_event_record_cl struct request_response_protocol_adapter_subscription_event_record { enum aws_protocol_adapter_subscription_event_type event_type; struct aws_byte_buf topic_filter; + int error_code; }; static void s_request_response_protocol_adapter_subscription_event_record_init( struct request_response_protocol_adapter_subscription_event_record *record, struct aws_allocator *allocator, - struct aws_byte_cursor topic_filter) { + enum aws_protocol_adapter_subscription_event_type event_type, + struct aws_byte_cursor topic_filter, + int error_code) { + AWS_ZERO_STRUCT(*record); + + record->event_type = event_type; + record->error_code = error_code; aws_byte_buf_init_copy_from_cursor(&record->topic_filter, allocator, topic_filter); } @@ -71,9 +78,9 @@ static void s_rr_mqtt5_protocol_adapter_test_on_subscription_event( void *user_data) { struct aws_request_response_mqtt5_adapter_test_fixture *fixture = user_data; - struct request_response_protocol_adapter_subscription_event_record record = {.event_type = event->event_type}; + struct request_response_protocol_adapter_subscription_event_record record; s_request_response_protocol_adapter_subscription_event_record_init( - &record, fixture->allocator, event->topic_filter); + &record, fixture->allocator, event->event_type, event->topic_filter, event->error_code); aws_mutex_lock(&fixture->lock); aws_array_list_push_back(&fixture->subscription_events, &record); @@ -117,11 +124,11 @@ static void s_rr_mqtt5_protocol_adapter_test_on_session_event( aws_condition_variable_notify_all(&fixture->signal); } -static void s_rr_mqtt5_protocol_adapter_test_on_publish_result(bool success, void *user_data) { +static void s_rr_mqtt5_protocol_adapter_test_on_publish_result(int error_code, void *user_data) { struct aws_request_response_mqtt5_adapter_test_fixture *fixture = user_data; aws_mutex_lock(&fixture->lock); - aws_array_list_push_back(&fixture->publish_results, &success); + aws_array_list_push_back(&fixture->publish_results, &error_code); aws_mutex_unlock(&fixture->lock); aws_condition_variable_notify_all(&fixture->signal); } @@ -162,7 +169,7 @@ static int s_aws_request_response_mqtt5_adapter_test_fixture_init( allocator, 10, sizeof(struct request_response_protocol_adapter_subscription_event_record)); - aws_array_list_init_dynamic(&fixture->publish_results, allocator, 10, sizeof(bool)); + aws_array_list_init_dynamic(&fixture->publish_results, allocator, 10, sizeof(int)); aws_mutex_init(&fixture->lock); aws_condition_variable_init(&fixture->signal); @@ -232,14 +239,22 @@ static bool s_do_subscription_events_contain(void *context) { struct request_response_protocol_adapter_subscription_event_record record; aws_array_list_get_at(&wait_context->fixture->subscription_events, &record, i); - if (record.event_type == wait_context->expected_event->event_type) { - struct aws_byte_cursor record_topic_filter = aws_byte_cursor_from_buf(&record.topic_filter); - struct aws_byte_cursor expected_topic_filter = - aws_byte_cursor_from_buf(&wait_context->expected_event->topic_filter); - if (aws_byte_cursor_eq(&record_topic_filter, &expected_topic_filter)) { - ++found; - } + if (record.event_type != wait_context->expected_event->event_type) { + continue; } + + if (record.error_code != wait_context->expected_event->error_code) { + continue; + } + + struct aws_byte_cursor record_topic_filter = aws_byte_cursor_from_buf(&record.topic_filter); + struct aws_byte_cursor expected_topic_filter = + aws_byte_cursor_from_buf(&wait_context->expected_event->topic_filter); + if (!aws_byte_cursor_eq(&record_topic_filter, &expected_topic_filter)) { + continue; + } + + ++found; } return found >= wait_context->expected_count; @@ -277,9 +292,11 @@ static bool s_do_session_events_contain(void *context) { struct aws_protocol_adapter_session_event record; aws_array_list_get_at(&wait_context->fixture->session_events, &record, i); - if (record.joined_session == wait_context->expected_event->joined_session) { - ++found; + if (record.joined_session != wait_context->expected_event->joined_session) { + continue; } + + ++found; } return found >= wait_context->expected_count; @@ -352,7 +369,7 @@ static void s_wait_for_incoming_publish_events_contains( } struct test_publish_result_wait_context { - bool expected_success; + int expected_error_code; size_t expected_count; struct aws_request_response_mqtt5_adapter_test_fixture *fixture; }; @@ -364,10 +381,10 @@ static bool s_do_publish_results_contain(void *context) { size_t num_events = aws_array_list_length(&wait_context->fixture->publish_results); for (size_t i = 0; i < num_events; ++i) { - bool success = false; - aws_array_list_get_at(&wait_context->fixture->publish_results, &success, i); + int error_code = AWS_ERROR_SUCCESS; + aws_array_list_get_at(&wait_context->fixture->publish_results, &error_code, i); - if (success == wait_context->expected_success) { + if (error_code == wait_context->expected_error_code) { ++found; } } @@ -377,11 +394,11 @@ static bool s_do_publish_results_contain(void *context) { static void s_wait_for_publish_results_contains( struct aws_request_response_mqtt5_adapter_test_fixture *fixture, - bool success, + int expected_error_code, size_t expected_count) { struct test_publish_result_wait_context context = { - .expected_success = success, + .expected_error_code = expected_error_code, .expected_count = expected_count, .fixture = fixture, }; @@ -420,6 +437,19 @@ static int s_aws_mqtt5_server_send_failed_suback_on_subscribe( return aws_mqtt5_mock_server_send_packet(connection, AWS_MQTT5_PT_SUBACK, &suback_view); } +static int s_test_type_to_expected_error_code(enum protocol_adapter_operation_test_type test_type) { + switch (test_type) { + case PAOTT_FAILURE_TIMEOUT: + return AWS_ERROR_MQTT_TIMEOUT; + case PAOTT_FAILURE_REASON_CODE: + return AWS_ERROR_MQTT_PROTOCOL_ADAPTER_FAILING_REASON_CODE; + case PAOTT_FAILURE_ERROR_CODE: + return AWS_ERROR_MQTT5_OPERATION_FAILED_DUE_TO_OFFLINE_QUEUE_POLICY; + default: + return AWS_ERROR_SUCCESS; + } +} + static int s_do_request_response_mqtt5_protocol_adapter_subscribe_test( struct aws_allocator *allocator, enum protocol_adapter_operation_test_type test_type) { @@ -457,12 +487,15 @@ static int s_do_request_response_mqtt5_protocol_adapter_subscribe_test( aws_wait_for_connected_lifecycle_event(&fixture.mqtt5_fixture); } - struct request_response_protocol_adapter_subscription_event_record expected_outcome = { - .event_type = (test_type == PAOTT_SUCCESS) ? AWS_PASET_SUBSCRIBE_SUCCESS : AWS_PASET_SUBSCRIBE_FAILURE, - }; + int expected_error_code = s_test_type_to_expected_error_code(test_type); - aws_byte_buf_init_copy_from_cursor( - &expected_outcome.topic_filter, allocator, aws_byte_cursor_from_c_str("hello/world")); + struct request_response_protocol_adapter_subscription_event_record expected_outcome; + s_request_response_protocol_adapter_subscription_event_record_init( + &expected_outcome, + allocator, + AWS_PASET_SUBSCRIBE, + aws_byte_cursor_from_c_str("hello/world"), + expected_error_code); struct aws_protocol_adapter_subscribe_options subscribe_options = { .topic_filter = aws_byte_cursor_from_buf(&expected_outcome.topic_filter), @@ -595,12 +628,15 @@ static int s_do_request_response_mqtt5_protocol_adapter_unsubscribe_test( aws_wait_for_connected_lifecycle_event(&fixture.mqtt5_fixture); } - struct request_response_protocol_adapter_subscription_event_record expected_outcome = { - .event_type = (test_type == PAOTT_SUCCESS) ? AWS_PASET_UNSUBSCRIBE_SUCCESS : AWS_PASET_UNSUBSCRIBE_FAILURE, - }; + int expected_error_code = s_test_type_to_expected_error_code(test_type); - aws_byte_buf_init_copy_from_cursor( - &expected_outcome.topic_filter, allocator, aws_byte_cursor_from_c_str("hello/world")); + struct request_response_protocol_adapter_subscription_event_record expected_outcome; + s_request_response_protocol_adapter_subscription_event_record_init( + &expected_outcome, + allocator, + AWS_PASET_UNSUBSCRIBE, + aws_byte_cursor_from_c_str("hello/world"), + expected_error_code); struct aws_protocol_adapter_unsubscribe_options unsubscribe_options = { .topic_filter = aws_byte_cursor_from_buf(&expected_outcome.topic_filter), @@ -743,7 +779,8 @@ static int s_do_request_response_mqtt5_protocol_adapter_publish_test( aws_mqtt_protocol_adapter_publish(fixture.protocol_adapter, &publish_options); - s_wait_for_publish_results_contains(&fixture, test_type == PAOTT_SUCCESS, 1); + int expected_error_code = s_test_type_to_expected_error_code(test_type); + s_wait_for_publish_results_contains(&fixture, expected_error_code, 1); s_aws_request_response_mqtt5_adapter_test_fixture_clean_up(&fixture); From ca0c14fbaa6401624e81f8d6a7dcb882f07abfe3 Mon Sep 17 00:00:00 2001 From: Bret Ambrose Date: Fri, 26 Jan 2024 11:17:11 -0800 Subject: [PATCH 19/19] Rebuild the branch --- include/aws/mqtt/private/client_impl.h | 4 + include/aws/mqtt/private/client_impl_shared.h | 4 +- include/aws/mqtt/private/mqtt311_listener.h | 180 +++ source/client.c | 7 +- source/client_channel_handler.c | 6 + source/client_impl_shared.c | 3 +- source/mqtt311_listener.c | 291 ++++ source/v5/mqtt5_listener.c | 1 + source/v5/mqtt5_to_mqtt3_adapter.c | 2 +- tests/CMakeLists.txt | 4 + tests/v3/connection_state_test.c | 1333 +++++------------ tests/v3/mqtt311_listener_test.c | 375 +++++ tests/v3/mqtt311_testing_utils.c | 580 +++++++ tests/v3/mqtt311_testing_utils.h | 155 ++ tests/v3/mqtt_mock_server_handler.c | 40 +- tests/v3/mqtt_mock_server_handler.h | 11 + tests/v5/mqtt5_testing_utils.c | 2 +- 17 files changed, 2005 insertions(+), 993 deletions(-) create mode 100644 include/aws/mqtt/private/mqtt311_listener.h create mode 100644 source/mqtt311_listener.c create mode 100644 tests/v3/mqtt311_listener_test.c create mode 100644 tests/v3/mqtt311_testing_utils.c create mode 100644 tests/v3/mqtt311_testing_utils.h diff --git a/include/aws/mqtt/private/client_impl.h b/include/aws/mqtt/private/client_impl.h index 1d0dd67a..ce041b2d 100644 --- a/include/aws/mqtt/private/client_impl.h +++ b/include/aws/mqtt/private/client_impl.h @@ -11,6 +11,7 @@ #include #include #include +#include #include #include @@ -255,6 +256,9 @@ struct aws_mqtt_client_connection_311_impl { aws_mqtt_on_operation_statistics_fn *on_any_operation_statistics; void *on_any_operation_statistics_ud; + /* listener callbacks */ + struct aws_mqtt311_callback_set_manager callback_manager; + /* Connection tasks. */ struct aws_mqtt_reconnect_task *reconnect_task; struct aws_channel_task ping_task; diff --git a/include/aws/mqtt/private/client_impl_shared.h b/include/aws/mqtt/private/client_impl_shared.h index 49961308..fa5cbeb3 100644 --- a/include/aws/mqtt/private/client_impl_shared.h +++ b/include/aws/mqtt/private/client_impl_shared.h @@ -122,7 +122,7 @@ struct aws_mqtt_client_connection_vtable { int (*get_stats_fn)(void *impl, struct aws_mqtt_connection_operation_statistics *stats); - enum aws_mqtt311_impl_type (*get_impl_type)(void *impl); + enum aws_mqtt311_impl_type (*get_impl_type)(const void *impl); }; struct aws_mqtt_client_connection { @@ -131,7 +131,7 @@ struct aws_mqtt_client_connection { }; AWS_MQTT_API enum aws_mqtt311_impl_type aws_mqtt_client_connection_get_impl_type( - struct aws_mqtt_client_connection *connection); + const struct aws_mqtt_client_connection *connection); AWS_MQTT_API uint64_t aws_mqtt_hash_uint16_t(const void *item); diff --git a/include/aws/mqtt/private/mqtt311_listener.h b/include/aws/mqtt/private/mqtt311_listener.h new file mode 100644 index 00000000..2a4f5b53 --- /dev/null +++ b/include/aws/mqtt/private/mqtt311_listener.h @@ -0,0 +1,180 @@ +/** + * Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. + * SPDX-License-Identifier: Apache-2.0. + */ + +#ifndef AWS_MQTT_MQTT311_LISTENER_H +#define AWS_MQTT_MQTT311_LISTENER_H + +#include + +#include +#include + +AWS_PUSH_SANE_WARNING_LEVEL + +/** + * Callback signature for when an mqtt311 listener has completely destroyed itself. + */ +typedef void(aws_mqtt311_listener_termination_completion_fn)(void *complete_ctx); + +/** + * A record that tracks MQTT311 client connection callbacks which can be dynamically injected via a listener. + * + * All the callbacks that are supported here are invoked only on the 311 connection's event loop. With the + * add/remove callback set also on the event loop, everything is correctly serialized without data races. + * + * If binding additional callbacks, they must only be invoked from the connection's event loop. + * + * We only listen to connection-success because the only connection-level event we care about is a failure + * to rejoin a session (which invalidates all subscriptions that were considered valid) + */ +struct aws_mqtt311_callback_set { + + /* Called from s_packet_handler_publish which is event-loop invoked */ + aws_mqtt_client_publish_received_fn *publish_received_handler; + + /* Called from s_packet_handler_connack which is event-loop invoked */ + aws_mqtt_client_on_connection_success_fn *connection_success_handler; + + void *user_data; +}; + +/** + * An internal type for managing chains of callbacks attached to an mqtt311 client connection. Supports chains for + * lifecycle events and incoming publish packet handling. + * + * Assumed to be owned and used only by an MQTT311 client connection. + */ +struct aws_mqtt311_callback_set_manager { + struct aws_allocator *allocator; + + struct aws_mqtt_client_connection *connection; + + struct aws_linked_list callback_set_entries; + + uint64_t next_callback_set_entry_id; +}; + +/** + * Configuration options for MQTT311 listener objects. + */ +struct aws_mqtt311_listener_config { + + /** + * MQTT311 client connection to listen to events on + */ + struct aws_mqtt_client_connection *connection; + + /** + * Callbacks to invoke when events occur on the MQTT311 client connection + */ + struct aws_mqtt311_callback_set listener_callbacks; + + /** + * Listener destruction is asynchronous and thus requires a termination callback and associated user data + * to notify the user that the listener has been fully destroyed and no further events will be received. + */ + aws_mqtt311_listener_termination_completion_fn *termination_callback; + void *termination_callback_user_data; +}; + +AWS_EXTERN_C_BEGIN + +/** + * Creates a new MQTT311 listener object. For as long as the listener lives, incoming publishes and lifecycle events + * will be forwarded to the callbacks configured on the listener. + * + * @param allocator allocator to use + * @param config listener configuration + * @return a new aws_mqtt311_listener object + */ +AWS_MQTT_API struct aws_mqtt311_listener *aws_mqtt311_listener_new( + struct aws_allocator *allocator, + struct aws_mqtt311_listener_config *config); + +/** + * Adds a reference to an mqtt311 listener. + * + * @param listener listener to add a reference to + * @return the listener object + */ +AWS_MQTT_API struct aws_mqtt311_listener *aws_mqtt311_listener_acquire(struct aws_mqtt311_listener *listener); + +/** + * Removes a reference to an mqtt311 listener. When the reference count drops to zero, the listener's asynchronous + * destruction will be started. + * + * @param listener listener to remove a reference from + * @return NULL + */ +AWS_MQTT_API struct aws_mqtt311_listener *aws_mqtt311_listener_release(struct aws_mqtt311_listener *listener); + +/** + * Initializes a callback set manager + */ +AWS_MQTT_API +void aws_mqtt311_callback_set_manager_init( + struct aws_mqtt311_callback_set_manager *manager, + struct aws_allocator *allocator, + struct aws_mqtt_client_connection *connection); + +/** + * Cleans up a callback set manager. + * + * aws_mqtt311_callback_set_manager_init must have been previously called or this will crash. + */ +AWS_MQTT_API +void aws_mqtt311_callback_set_manager_clean_up(struct aws_mqtt311_callback_set_manager *manager); + +/** + * Adds a callback set to the front of the handler chain. Returns an integer id that can be used to selectively + * remove the callback set from the manager. + * + * May only be called on the client's event loop thread. + */ +AWS_MQTT_API +uint64_t aws_mqtt311_callback_set_manager_push_front( + struct aws_mqtt311_callback_set_manager *manager, + struct aws_mqtt311_callback_set *callback_set); + +/** + * Removes a callback set from the handler chain. + * + * May only be called on the client's event loop thread. + */ +AWS_MQTT_API +void aws_mqtt311_callback_set_manager_remove( + struct aws_mqtt311_callback_set_manager *manager, + uint64_t callback_set_id); + +/** + * Walks the incoming publish handler chain for an MQTT311 connection, invoking each in sequence. + * + * May only be called on the client's event loop thread. + */ +AWS_MQTT_API +void aws_mqtt311_callback_set_manager_on_publish_received( + struct aws_mqtt311_callback_set_manager *manager, + const struct aws_byte_cursor *topic, + const struct aws_byte_cursor *payload, + bool dup, + enum aws_mqtt_qos qos, + bool retain); + +/** + * Invokes a connection success event on each listener in the manager's collection of callback sets. + * + * May only be called on the client's event loop thread. + */ +AWS_MQTT_API +void aws_mqtt311_callback_set_manager_on_connection_success( + struct aws_mqtt311_callback_set_manager *manager, + enum aws_mqtt_connect_return_code return_code, + bool rejoined_session); + +AWS_EXTERN_C_END + +AWS_POP_SANE_WARNING_LEVEL + +#endif /* AWS_MQTT_MQTT311_LISTENER_H */ diff --git a/source/client.c b/source/client.c index 631a26c0..8a8bc386 100644 --- a/source/client.c +++ b/source/client.c @@ -259,6 +259,7 @@ static void s_mqtt_client_shutdown( (void)channel; struct aws_mqtt_client_connection_311_impl *connection = user_data; + AWS_FATAL_ASSERT(aws_event_loop_thread_is_callers_thread(connection->loop)); AWS_LOGF_TRACE( AWS_LS_MQTT_CLIENT, "id=%p: Channel has been shutdown with error code %d", (void *)connection, error_code); @@ -801,6 +802,8 @@ static void s_mqtt_client_connection_destroy_final(struct aws_mqtt_client_connec termination_handler_user_data = connection->on_termination_ud; } + aws_mqtt311_callback_set_manager_clean_up(&connection->callback_manager); + /* If the reconnect_task isn't freed, free it */ if (connection->reconnect_task) { aws_mem_release(connection->reconnect_task->allocator, connection->reconnect_task); @@ -3220,7 +3223,7 @@ static void s_aws_mqtt_client_connection_311_release(void *impl) { aws_ref_count_release(&connection->ref_count); } -enum aws_mqtt311_impl_type s_aws_mqtt_client_connection_3_get_impl(void *impl) { +enum aws_mqtt311_impl_type s_aws_mqtt_client_connection_3_get_impl(const void *impl) { (void)impl; return AWS_MQTT311_IT_311_CONNECTION; @@ -3351,6 +3354,8 @@ struct aws_mqtt_client_connection *aws_mqtt_client_connection_new(struct aws_mqt connection->handler.vtable = aws_mqtt_get_client_channel_vtable(); connection->handler.impl = connection; + aws_mqtt311_callback_set_manager_init(&connection->callback_manager, connection->allocator, &connection->base); + return &connection->base; failed_init_outstanding_requests_table: diff --git a/source/client_channel_handler.c b/source/client_channel_handler.c index 543f0950..b95a43fc 100644 --- a/source/client_channel_handler.c +++ b/source/client_channel_handler.c @@ -263,6 +263,9 @@ static int s_packet_handler_connack(struct aws_byte_cursor message_cursor, void MQTT_CLIENT_CALL_CALLBACK_ARGS( connection, on_connection_success, connack.connect_return_code, connack.session_present); + aws_mqtt311_callback_set_manager_on_connection_success( + &connection->callback_manager, connack.connect_return_code, connack.session_present); + AWS_LOGF_TRACE(AWS_LS_MQTT_CLIENT, "id=%p: connection callback completed", (void *)connection); s_update_next_ping_time(connection); @@ -291,6 +294,9 @@ static int s_packet_handler_publish(struct aws_byte_cursor message_cursor, void MQTT_CLIENT_CALL_CALLBACK_ARGS(connection, on_any_publish, &publish.topic_name, &publish.payload, dup, qos, retain); + aws_mqtt311_callback_set_manager_on_publish_received( + &connection->callback_manager, &publish.topic_name, &publish.payload, dup, qos, retain); + AWS_LOGF_TRACE( AWS_LS_MQTT_CLIENT, "id=%p: publish received with msg id=%" PRIu16 " dup=%d qos=%d retain=%d payload-size=%zu topic=" PRInSTR, diff --git a/source/client_impl_shared.c b/source/client_impl_shared.c index 0f0e70a2..525fb7e4 100644 --- a/source/client_impl_shared.c +++ b/source/client_impl_shared.c @@ -204,7 +204,8 @@ int aws_mqtt_client_connection_get_stats( return (*connection->vtable->get_stats_fn)(connection->impl, stats); } -enum aws_mqtt311_impl_type aws_mqtt_client_connection_get_impl_type(struct aws_mqtt_client_connection *connection) { +enum aws_mqtt311_impl_type aws_mqtt_client_connection_get_impl_type( + const struct aws_mqtt_client_connection *connection) { return (*connection->vtable->get_impl_type)(connection->impl); } diff --git a/source/mqtt311_listener.c b/source/mqtt311_listener.c new file mode 100644 index 00000000..caf9b650 --- /dev/null +++ b/source/mqtt311_listener.c @@ -0,0 +1,291 @@ +/** + * Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. + * SPDX-License-Identifier: Apache-2.0. + */ + +#include + +#include +#include +#include +#include +#include + +#include + +static struct aws_event_loop *s_mqtt_client_connection_get_event_loop( + const struct aws_mqtt_client_connection *connection) { + AWS_FATAL_ASSERT(aws_mqtt_client_connection_get_impl_type(connection) == AWS_MQTT311_IT_311_CONNECTION); + + struct aws_mqtt_client_connection_311_impl *connection_impl = connection->impl; + + return connection_impl->loop; +} + +struct aws_mqtt311_listener { + struct aws_allocator *allocator; + + struct aws_ref_count ref_count; + + struct aws_mqtt311_listener_config config; + + uint64_t callback_set_id; + + struct aws_task initialize_task; + struct aws_task terminate_task; +}; + +static void s_mqtt311_listener_destroy(struct aws_mqtt311_listener *listener) { + + aws_mqtt_client_connection_release(listener->config.connection); + + aws_mqtt311_listener_termination_completion_fn *termination_callback = listener->config.termination_callback; + void *termination_callback_user_data = listener->config.termination_callback_user_data; + + aws_mem_release(listener->allocator, listener); + + if (termination_callback != NULL) { + (*termination_callback)(termination_callback_user_data); + } +} + +static void s_mqtt311_listener_initialize_task_fn(struct aws_task *task, void *arg, enum aws_task_status task_status) { + (void)task; + + struct aws_mqtt311_listener *listener = arg; + + if (task_status == AWS_TASK_STATUS_RUN_READY) { + struct aws_mqtt_client_connection_311_impl *connection_impl = listener->config.connection->impl; + listener->callback_set_id = aws_mqtt311_callback_set_manager_push_front( + &connection_impl->callback_manager, &listener->config.listener_callbacks); + AWS_LOGF_INFO( + AWS_LS_MQTT_GENERAL, + "id=%p: Mqtt311 Listener initialized, listener id=%p", + (void *)listener->config.connection, + (void *)listener); + aws_mqtt311_listener_release(listener); + } else { + s_mqtt311_listener_destroy(listener); + } +} + +static void s_mqtt311_listener_terminate_task_fn(struct aws_task *task, void *arg, enum aws_task_status task_status) { + (void)task; + + struct aws_mqtt311_listener *listener = arg; + + if (task_status == AWS_TASK_STATUS_RUN_READY) { + struct aws_mqtt_client_connection_311_impl *connection_impl = listener->config.connection->impl; + aws_mqtt311_callback_set_manager_remove(&connection_impl->callback_manager, listener->callback_set_id); + } + + AWS_LOGF_INFO( + AWS_LS_MQTT_GENERAL, + "id=%p: Mqtt311 Listener terminated, listener id=%p", + (void *)listener->config.connection, + (void *)listener); + + s_mqtt311_listener_destroy(listener); +} + +static void s_aws_mqtt311_listener_on_zero_ref_count(void *context) { + struct aws_mqtt311_listener *listener = context; + + aws_event_loop_schedule_task_now( + s_mqtt_client_connection_get_event_loop(listener->config.connection), &listener->terminate_task); +} + +struct aws_mqtt311_listener *aws_mqtt311_listener_new( + struct aws_allocator *allocator, + struct aws_mqtt311_listener_config *config) { + if (config->connection == NULL) { + aws_raise_error(AWS_ERROR_INVALID_ARGUMENT); + return NULL; + } + + if (aws_mqtt_client_connection_get_impl_type(config->connection) != AWS_MQTT311_IT_311_CONNECTION) { + aws_raise_error(AWS_ERROR_INVALID_ARGUMENT); + return NULL; + } + + struct aws_mqtt311_listener *listener = aws_mem_calloc(allocator, 1, sizeof(struct aws_mqtt311_listener)); + + listener->allocator = allocator; + listener->config = *config; + + aws_mqtt_client_connection_acquire(config->connection); + aws_ref_count_init(&listener->ref_count, listener, s_aws_mqtt311_listener_on_zero_ref_count); + + aws_task_init( + &listener->initialize_task, s_mqtt311_listener_initialize_task_fn, listener, "Mqtt311ListenerInitialize"); + aws_task_init( + &listener->terminate_task, s_mqtt311_listener_terminate_task_fn, listener, "Mqtt311ListenerTerminate"); + + aws_mqtt311_listener_acquire(listener); + aws_event_loop_schedule_task_now( + s_mqtt_client_connection_get_event_loop(config->connection), &listener->initialize_task); + + return listener; +} + +struct aws_mqtt311_listener *aws_mqtt311_listener_acquire(struct aws_mqtt311_listener *listener) { + if (listener != NULL) { + aws_ref_count_acquire(&listener->ref_count); + } + + return listener; +} + +struct aws_mqtt311_listener *aws_mqtt311_listener_release(struct aws_mqtt311_listener *listener) { + if (listener != NULL) { + aws_ref_count_release(&listener->ref_count); + } + + return NULL; +} + +struct aws_mqtt311_callback_set_entry { + struct aws_allocator *allocator; + + struct aws_linked_list_node node; + + uint64_t id; + + struct aws_mqtt311_callback_set callbacks; +}; + +void aws_mqtt311_callback_set_manager_init( + struct aws_mqtt311_callback_set_manager *manager, + struct aws_allocator *allocator, + struct aws_mqtt_client_connection *connection) { + + manager->allocator = allocator; + manager->connection = connection; /* no need to ref count, this is assumed to be owned by the client connection */ + manager->next_callback_set_entry_id = 1; + + aws_linked_list_init(&manager->callback_set_entries); +} + +void aws_mqtt311_callback_set_manager_clean_up(struct aws_mqtt311_callback_set_manager *manager) { + struct aws_linked_list_node *node = aws_linked_list_begin(&manager->callback_set_entries); + while (node != aws_linked_list_end(&manager->callback_set_entries)) { + struct aws_mqtt311_callback_set_entry *entry = + AWS_CONTAINER_OF(node, struct aws_mqtt311_callback_set_entry, node); + node = aws_linked_list_next(node); + + aws_linked_list_remove(&entry->node); + aws_mem_release(entry->allocator, entry); + } +} + +static struct aws_mqtt311_callback_set_entry *s_new_311_callback_set_entry( + struct aws_mqtt311_callback_set_manager *manager, + struct aws_mqtt311_callback_set *callback_set) { + struct aws_mqtt311_callback_set_entry *entry = + aws_mem_calloc(manager->allocator, 1, sizeof(struct aws_mqtt311_callback_set_entry)); + + entry->allocator = manager->allocator; + entry->id = manager->next_callback_set_entry_id++; + entry->callbacks = *callback_set; + + AWS_LOGF_INFO( + AWS_LS_MQTT_GENERAL, + "id=%p: MQTT311 callback manager created new entry :%" PRIu64, + (void *)manager->connection, + entry->id); + + return entry; +} + +uint64_t aws_mqtt311_callback_set_manager_push_front( + struct aws_mqtt311_callback_set_manager *manager, + struct aws_mqtt311_callback_set *callback_set) { + + AWS_FATAL_ASSERT( + aws_event_loop_thread_is_callers_thread(s_mqtt_client_connection_get_event_loop(manager->connection))); + + struct aws_mqtt311_callback_set_entry *entry = s_new_311_callback_set_entry(manager, callback_set); + + aws_linked_list_push_front(&manager->callback_set_entries, &entry->node); + + return entry->id; +} + +void aws_mqtt311_callback_set_manager_remove( + struct aws_mqtt311_callback_set_manager *manager, + uint64_t callback_set_id) { + + AWS_FATAL_ASSERT( + aws_event_loop_thread_is_callers_thread(s_mqtt_client_connection_get_event_loop(manager->connection))); + + struct aws_linked_list_node *node = aws_linked_list_begin(&manager->callback_set_entries); + while (node != aws_linked_list_end(&manager->callback_set_entries)) { + struct aws_mqtt311_callback_set_entry *entry = + AWS_CONTAINER_OF(node, struct aws_mqtt311_callback_set_entry, node); + node = aws_linked_list_next(node); + + if (entry->id == callback_set_id) { + aws_linked_list_remove(&entry->node); + + AWS_LOGF_INFO( + AWS_LS_MQTT_GENERAL, + "id=%p: MQTT311 callback manager removed entry id=%" PRIu64, + (void *)manager->connection, + entry->id); + aws_mem_release(entry->allocator, entry); + return; + } + } + AWS_LOGF_INFO( + AWS_LS_MQTT_GENERAL, + "id=%p: MQTT311 callback manager failed to remove entry id=%" PRIu64 ", callback set id not found.", + (void *)manager->connection, + callback_set_id); +} + +void aws_mqtt311_callback_set_manager_on_publish_received( + struct aws_mqtt311_callback_set_manager *manager, + const struct aws_byte_cursor *topic, + const struct aws_byte_cursor *payload, + bool dup, + enum aws_mqtt_qos qos, + bool retain) { + + struct aws_mqtt_client_connection_311_impl *connection_impl = manager->connection->impl; + AWS_FATAL_ASSERT(aws_event_loop_thread_is_callers_thread(connection_impl->loop)); + + struct aws_linked_list_node *node = aws_linked_list_begin(&manager->callback_set_entries); + while (node != aws_linked_list_end(&manager->callback_set_entries)) { + struct aws_mqtt311_callback_set_entry *entry = + AWS_CONTAINER_OF(node, struct aws_mqtt311_callback_set_entry, node); + node = aws_linked_list_next(node); + + struct aws_mqtt311_callback_set *callback_set = &entry->callbacks; + if (callback_set->publish_received_handler != NULL) { + (*callback_set->publish_received_handler)( + manager->connection, topic, payload, dup, qos, retain, callback_set->user_data); + } + } +} + +void aws_mqtt311_callback_set_manager_on_connection_success( + struct aws_mqtt311_callback_set_manager *manager, + enum aws_mqtt_connect_return_code return_code, + bool rejoined_session) { + + struct aws_mqtt_client_connection_311_impl *connection_impl = manager->connection->impl; + AWS_FATAL_ASSERT(aws_event_loop_thread_is_callers_thread(connection_impl->loop)); + + struct aws_linked_list_node *node = aws_linked_list_begin(&manager->callback_set_entries); + while (node != aws_linked_list_end(&manager->callback_set_entries)) { + struct aws_mqtt311_callback_set_entry *entry = + AWS_CONTAINER_OF(node, struct aws_mqtt311_callback_set_entry, node); + node = aws_linked_list_next(node); + + struct aws_mqtt311_callback_set *callback_set = &entry->callbacks; + if (callback_set->connection_success_handler != NULL) { + (*callback_set->connection_success_handler)( + manager->connection, return_code, rejoined_session, callback_set->user_data); + } + } +} diff --git a/source/v5/mqtt5_listener.c b/source/v5/mqtt5_listener.c index 04170394..ebb45b81 100644 --- a/source/v5/mqtt5_listener.c +++ b/source/v5/mqtt5_listener.c @@ -84,6 +84,7 @@ struct aws_mqtt5_listener *aws_mqtt5_listener_new( struct aws_allocator *allocator, struct aws_mqtt5_listener_config *config) { if (config->client == NULL) { + aws_raise_error(AWS_ERROR_INVALID_ARGUMENT); return NULL; } diff --git a/source/v5/mqtt5_to_mqtt3_adapter.c b/source/v5/mqtt5_to_mqtt3_adapter.c index 2cb0451d..72128a97 100644 --- a/source/v5/mqtt5_to_mqtt3_adapter.c +++ b/source/v5/mqtt5_to_mqtt3_adapter.c @@ -2854,7 +2854,7 @@ static uint16_t s_aws_mqtt_5_resubscribe_existing_topics( return 0; } -enum aws_mqtt311_impl_type s_aws_mqtt_client_connection_5_get_impl(void *impl) { +enum aws_mqtt311_impl_type s_aws_mqtt_client_connection_5_get_impl(const void *impl) { (void)impl; return AWS_MQTT311_IT_5_ADAPTER; diff --git a/tests/CMakeLists.txt b/tests/CMakeLists.txt index d93b7427..e52138a6 100644 --- a/tests/CMakeLists.txt +++ b/tests/CMakeLists.txt @@ -459,6 +459,10 @@ add_test_case(request_response_mqtt5_protocol_adapter_session_event_rejoin) add_test_case(request_response_mqtt5_protocol_adapter_incoming_publish) add_test_case(request_response_mqtt5_protocol_adapter_shutdown_while_pending) +add_test_case(mqtt311_listener_connection_success_event_no_session) +add_test_case(mqtt311_listener_connection_success_event_with_session) +add_test_case(mqtt311_listener_publish_event) + generate_test_driver(${PROJECT_NAME}-tests) set(TEST_PAHO_CLIENT_BINARY_NAME ${PROJECT_NAME}-paho-client) diff --git a/tests/v3/connection_state_test.c b/tests/v3/connection_state_test.c index 6a3e086f..65f8b9e7 100644 --- a/tests/v3/connection_state_test.c +++ b/tests/v3/connection_state_test.c @@ -3,661 +3,22 @@ * SPDX-License-Identifier: Apache-2.0. */ -#include "mqtt_mock_server_handler.h" - -#include +#include -#include -#include -#include +#include "mqtt311_testing_utils.h" +#include "mqtt_mock_server_handler.h" #include -#include - #include -#include - -static const int TEST_LOG_SUBJECT = 60000; -static const int ONE_SEC = 1000000000; -// The value is extract from aws-c-mqtt/source/client.c -static const int AWS_RESET_RECONNECT_BACKOFF_DELAY_SECONDS = 10; -static const uint64_t RECONNECT_BACKOFF_DELAY_ERROR_MARGIN_NANO_SECONDS = 500000000; -#define DEFAULT_MIN_RECONNECT_DELAY_SECONDS 1 - -#define DEFAULT_TEST_PING_TIMEOUT_MS 1000 -#define DEFAULT_TEST_KEEP_ALIVE_S 2 - -struct received_publish_packet { - struct aws_byte_buf topic; - struct aws_byte_buf payload; - bool dup; - enum aws_mqtt_qos qos; - bool retain; -}; - -struct mqtt_connection_state_test { - struct aws_allocator *allocator; - struct aws_channel *server_channel; - struct aws_channel_handler *mock_server; - struct aws_client_bootstrap *client_bootstrap; - struct aws_server_bootstrap *server_bootstrap; - struct aws_event_loop_group *el_group; - struct aws_host_resolver *host_resolver; - struct aws_socket_endpoint endpoint; - struct aws_socket *listener; - struct aws_mqtt_client *mqtt_client; - struct aws_mqtt_client_connection *mqtt_connection; - struct aws_socket_options socket_options; - - bool session_present; - bool connection_completed; - bool connection_success; - bool connection_failure; - bool client_disconnect_completed; - bool server_disconnect_completed; - bool connection_interrupted; - bool connection_resumed; - bool subscribe_completed; - bool listener_destroyed; - bool connection_terminated; - int interruption_error; - int subscribe_complete_error; - int op_complete_error; - enum aws_mqtt_connect_return_code mqtt_return_code; - int error; - struct aws_condition_variable cvar; - struct aws_mutex lock; - /* any published messages from mock server, that you may not subscribe to. (Which should not happen in real life) */ - struct aws_array_list any_published_messages; /* list of struct received_publish_packet */ - size_t any_publishes_received; - size_t expected_any_publishes; - /* the published messages from mock server, that you did subscribe to. */ - struct aws_array_list published_messages; /* list of struct received_publish_packet */ - size_t publishes_received; - size_t expected_publishes; - /* The returned QoS from mock server */ - struct aws_array_list qos_returned; /* list of uint_8 */ - size_t ops_completed; - size_t expected_ops_completed; - size_t connection_close_calls; /* All of the times on_connection_closed has been called */ - - size_t connection_termination_calls; /* How many times on_connection_termination has been called, should be 1 */ -}; - static struct mqtt_connection_state_test test_data = {0}; -static void s_on_any_publish_received( - struct aws_mqtt_client_connection *connection, - const struct aws_byte_cursor *topic, - const struct aws_byte_cursor *payload, - bool dup, - enum aws_mqtt_qos qos, - bool retain, - void *userdata); - -static void s_on_incoming_channel_setup_fn( - struct aws_server_bootstrap *bootstrap, - int error_code, - struct aws_channel *channel, - void *user_data) { - (void)bootstrap; - struct mqtt_connection_state_test *state_test_data = user_data; - - state_test_data->error = error_code; - - if (!error_code) { - aws_mutex_lock(&state_test_data->lock); - state_test_data->server_disconnect_completed = false; - aws_mutex_unlock(&state_test_data->lock); - AWS_LOGF_DEBUG(TEST_LOG_SUBJECT, "server channel setup completed"); - - state_test_data->server_channel = channel; - struct aws_channel_slot *test_handler_slot = aws_channel_slot_new(channel); - aws_channel_slot_insert_end(channel, test_handler_slot); - mqtt_mock_server_handler_update_slot(state_test_data->mock_server, test_handler_slot); - aws_channel_slot_set_handler(test_handler_slot, state_test_data->mock_server); - } -} - -static void s_on_incoming_channel_shutdown_fn( - struct aws_server_bootstrap *bootstrap, - int error_code, - struct aws_channel *channel, - void *user_data) { - (void)bootstrap; - (void)error_code; - (void)channel; - struct mqtt_connection_state_test *state_test_data = user_data; - aws_mutex_lock(&state_test_data->lock); - state_test_data->server_disconnect_completed = true; - AWS_LOGF_DEBUG(TEST_LOG_SUBJECT, "server channel shutdown completed"); - aws_mutex_unlock(&state_test_data->lock); - aws_condition_variable_notify_one(&state_test_data->cvar); -} - -static void s_on_listener_destroy(struct aws_server_bootstrap *bootstrap, void *user_data) { - (void)bootstrap; - struct mqtt_connection_state_test *state_test_data = user_data; - aws_mutex_lock(&state_test_data->lock); - state_test_data->listener_destroyed = true; - aws_mutex_unlock(&state_test_data->lock); - aws_condition_variable_notify_one(&state_test_data->cvar); -} - -static bool s_is_listener_destroyed(void *arg) { - struct mqtt_connection_state_test *state_test_data = arg; - return state_test_data->listener_destroyed; -} - -static void s_wait_on_listener_cleanup(struct mqtt_connection_state_test *state_test_data) { - aws_mutex_lock(&state_test_data->lock); - aws_condition_variable_wait_pred( - &state_test_data->cvar, &state_test_data->lock, s_is_listener_destroyed, state_test_data); - aws_mutex_unlock(&state_test_data->lock); -} - -static void s_on_connection_interrupted(struct aws_mqtt_client_connection *connection, int error_code, void *userdata) { - (void)connection; - (void)error_code; - struct mqtt_connection_state_test *state_test_data = userdata; - - aws_mutex_lock(&state_test_data->lock); - state_test_data->connection_interrupted = true; - state_test_data->interruption_error = error_code; - aws_mutex_unlock(&state_test_data->lock); - AWS_LOGF_DEBUG(TEST_LOG_SUBJECT, "connection interrupted"); - aws_condition_variable_notify_one(&state_test_data->cvar); -} - -static bool s_is_connection_interrupted(void *arg) { - struct mqtt_connection_state_test *state_test_data = arg; - return state_test_data->connection_interrupted; -} - -static void s_wait_for_interrupt_to_complete(struct mqtt_connection_state_test *state_test_data) { - aws_mutex_lock(&state_test_data->lock); - aws_condition_variable_wait_pred( - &state_test_data->cvar, &state_test_data->lock, s_is_connection_interrupted, state_test_data); - state_test_data->connection_interrupted = false; - aws_mutex_unlock(&state_test_data->lock); -} - -static void s_on_connection_resumed( - struct aws_mqtt_client_connection *connection, - enum aws_mqtt_connect_return_code return_code, - bool session_present, - void *userdata) { - (void)connection; - (void)return_code; - (void)session_present; - AWS_LOGF_DEBUG(TEST_LOG_SUBJECT, "reconnect completed"); - - struct mqtt_connection_state_test *state_test_data = userdata; - - aws_mutex_lock(&state_test_data->lock); - state_test_data->connection_resumed = true; - aws_mutex_unlock(&state_test_data->lock); - aws_condition_variable_notify_one(&state_test_data->cvar); -} - -static bool s_is_connection_resumed(void *arg) { - struct mqtt_connection_state_test *state_test_data = arg; - return state_test_data->connection_resumed; -} - -static void s_wait_for_reconnect_to_complete(struct mqtt_connection_state_test *state_test_data) { - aws_mutex_lock(&state_test_data->lock); - aws_condition_variable_wait_pred( - &state_test_data->cvar, &state_test_data->lock, s_is_connection_resumed, state_test_data); - state_test_data->connection_resumed = false; - aws_mutex_unlock(&state_test_data->lock); -} - -static void s_on_connection_success( - struct aws_mqtt_client_connection *connection, - enum aws_mqtt_connect_return_code return_code, - bool session_present, - void *userdata) { - (void)connection; - struct mqtt_connection_state_test *state_test_data = userdata; - aws_mutex_lock(&state_test_data->lock); - - state_test_data->session_present = session_present; - state_test_data->mqtt_return_code = return_code; - state_test_data->connection_success = true; - aws_mutex_unlock(&state_test_data->lock); - - aws_condition_variable_notify_one(&state_test_data->cvar); -} - -static void s_on_connection_failure(struct aws_mqtt_client_connection *connection, int error_code, void *userdata) { - (void)connection; - struct mqtt_connection_state_test *state_test_data = userdata; - aws_mutex_lock(&state_test_data->lock); - - state_test_data->error = error_code; - state_test_data->connection_failure = true; - aws_mutex_unlock(&state_test_data->lock); - - aws_condition_variable_notify_one(&state_test_data->cvar); -} - -static bool s_is_connection_succeed(void *arg) { - struct mqtt_connection_state_test *state_test_data = arg; - return state_test_data->connection_success; -} - -static bool s_is_connection_failed(void *arg) { - struct mqtt_connection_state_test *state_test_data = arg; - return state_test_data->connection_failure; -} - -static void s_wait_for_connection_to_succeed(struct mqtt_connection_state_test *state_test_data) { - aws_mutex_lock(&state_test_data->lock); - aws_condition_variable_wait_pred( - &state_test_data->cvar, &state_test_data->lock, s_is_connection_succeed, state_test_data); - state_test_data->connection_success = false; - aws_mutex_unlock(&state_test_data->lock); -} - -static void s_wait_for_connection_to_fail(struct mqtt_connection_state_test *state_test_data) { - aws_mutex_lock(&state_test_data->lock); - aws_condition_variable_wait_pred( - &state_test_data->cvar, &state_test_data->lock, s_is_connection_failed, state_test_data); - state_test_data->connection_failure = false; - aws_mutex_unlock(&state_test_data->lock); -} - -static bool s_is_termination_completed(void *arg) { - struct mqtt_connection_state_test *state_test_data = arg; - return state_test_data->connection_terminated; -} - -static void s_wait_for_termination_to_complete(struct mqtt_connection_state_test *state_test_data) { - aws_mutex_lock(&state_test_data->lock); - aws_condition_variable_wait_pred( - &state_test_data->cvar, &state_test_data->lock, s_is_termination_completed, state_test_data); - state_test_data->connection_terminated = false; - aws_mutex_unlock(&state_test_data->lock); -} - -static void s_on_connection_termination_fn(void *userdata) { - struct mqtt_connection_state_test *state_test_data = (struct mqtt_connection_state_test *)userdata; - - aws_mutex_lock(&state_test_data->lock); - state_test_data->connection_termination_calls += 1; - state_test_data->connection_terminated = true; - aws_mutex_unlock(&state_test_data->lock); - aws_condition_variable_notify_one(&state_test_data->cvar); -} - -/** sets up a unix domain socket server and socket options. Creates an mqtt connection configured to use - * the domain socket. - */ static int s_setup_mqtt_server_fn(struct aws_allocator *allocator, void *ctx) { - aws_mqtt_library_init(allocator); - - struct mqtt_connection_state_test *state_test_data = ctx; - - AWS_ZERO_STRUCT(*state_test_data); - - state_test_data->allocator = allocator; - state_test_data->el_group = aws_event_loop_group_new_default(allocator, 1, NULL); - - state_test_data->mock_server = new_mqtt_mock_server(allocator); - ASSERT_NOT_NULL(state_test_data->mock_server); - - state_test_data->server_bootstrap = aws_server_bootstrap_new(allocator, state_test_data->el_group); - ASSERT_NOT_NULL(state_test_data->server_bootstrap); - - struct aws_socket_options socket_options = { - .connect_timeout_ms = 100, - .domain = AWS_SOCKET_LOCAL, - }; - - state_test_data->socket_options = socket_options; - ASSERT_SUCCESS(aws_condition_variable_init(&state_test_data->cvar)); - ASSERT_SUCCESS(aws_mutex_init(&state_test_data->lock)); - - aws_socket_endpoint_init_local_address_for_test(&state_test_data->endpoint); - - struct aws_server_socket_channel_bootstrap_options server_bootstrap_options = { - .bootstrap = state_test_data->server_bootstrap, - .host_name = state_test_data->endpoint.address, - .port = state_test_data->endpoint.port, - .socket_options = &state_test_data->socket_options, - .incoming_callback = s_on_incoming_channel_setup_fn, - .shutdown_callback = s_on_incoming_channel_shutdown_fn, - .destroy_callback = s_on_listener_destroy, - .user_data = state_test_data, - }; - state_test_data->listener = aws_server_bootstrap_new_socket_listener(&server_bootstrap_options); - - ASSERT_NOT_NULL(state_test_data->listener); - - struct aws_host_resolver_default_options resolver_options = { - .el_group = state_test_data->el_group, - .max_entries = 1, - }; - state_test_data->host_resolver = aws_host_resolver_new_default(allocator, &resolver_options); - - struct aws_client_bootstrap_options bootstrap_options = { - .event_loop_group = state_test_data->el_group, - .user_data = state_test_data, - .host_resolver = state_test_data->host_resolver, - }; - - state_test_data->client_bootstrap = aws_client_bootstrap_new(allocator, &bootstrap_options); - - state_test_data->mqtt_client = aws_mqtt_client_new(allocator, state_test_data->client_bootstrap); - state_test_data->mqtt_connection = aws_mqtt_client_connection_new(state_test_data->mqtt_client); - ASSERT_NOT_NULL(state_test_data->mqtt_connection); - - ASSERT_SUCCESS(aws_mqtt_client_connection_set_connection_interruption_handlers( - state_test_data->mqtt_connection, - s_on_connection_interrupted, - state_test_data, - s_on_connection_resumed, - state_test_data)); - - ASSERT_SUCCESS(aws_mqtt_client_connection_set_connection_result_handlers( - state_test_data->mqtt_connection, - s_on_connection_success, - state_test_data, - s_on_connection_failure, - state_test_data)); - - ASSERT_SUCCESS(aws_mqtt_client_connection_set_on_any_publish_handler( - state_test_data->mqtt_connection, s_on_any_publish_received, state_test_data)); - - ASSERT_SUCCESS(aws_array_list_init_dynamic( - &state_test_data->published_messages, allocator, 4, sizeof(struct received_publish_packet))); - ASSERT_SUCCESS(aws_array_list_init_dynamic( - &state_test_data->any_published_messages, allocator, 4, sizeof(struct received_publish_packet))); - ASSERT_SUCCESS(aws_array_list_init_dynamic(&state_test_data->qos_returned, allocator, 2, sizeof(uint8_t))); - - ASSERT_SUCCESS(aws_mqtt_client_connection_set_connection_termination_handler( - state_test_data->mqtt_connection, s_on_connection_termination_fn, state_test_data)); - - return AWS_OP_SUCCESS; -} - -static void s_received_publish_packet_list_clean_up(struct aws_array_list *list) { - for (size_t i = 0; i < aws_array_list_length(list); ++i) { - struct received_publish_packet *val_ptr = NULL; - aws_array_list_get_at_ptr(list, (void **)&val_ptr, i); - aws_byte_buf_clean_up(&val_ptr->payload); - aws_byte_buf_clean_up(&val_ptr->topic); - } - aws_array_list_clean_up(list); + return aws_test311_setup_mqtt_server_fn(allocator, ctx); } static int s_clean_up_mqtt_server_fn(struct aws_allocator *allocator, int setup_result, void *ctx) { - (void)allocator; - - if (!setup_result) { - struct mqtt_connection_state_test *state_test_data = ctx; - - s_received_publish_packet_list_clean_up(&state_test_data->published_messages); - s_received_publish_packet_list_clean_up(&state_test_data->any_published_messages); - aws_array_list_clean_up(&state_test_data->qos_returned); - aws_mqtt_client_connection_release(state_test_data->mqtt_connection); - - s_wait_for_termination_to_complete(state_test_data); - ASSERT_UINT_EQUALS(1, state_test_data->connection_termination_calls); - - aws_mqtt_client_release(state_test_data->mqtt_client); - aws_client_bootstrap_release(state_test_data->client_bootstrap); - aws_host_resolver_release(state_test_data->host_resolver); - aws_server_bootstrap_destroy_socket_listener(state_test_data->server_bootstrap, state_test_data->listener); - s_wait_on_listener_cleanup(state_test_data); - aws_server_bootstrap_release(state_test_data->server_bootstrap); - aws_event_loop_group_release(state_test_data->el_group); - destroy_mqtt_mock_server(state_test_data->mock_server); - } - - aws_mqtt_library_clean_up(); - return AWS_OP_SUCCESS; -} - -static void s_on_connection_complete_fn( - struct aws_mqtt_client_connection *connection, - int error_code, - enum aws_mqtt_connect_return_code return_code, - bool session_present, - void *userdata) { - (void)connection; - struct mqtt_connection_state_test *state_test_data = userdata; - aws_mutex_lock(&state_test_data->lock); - - state_test_data->session_present = session_present; - state_test_data->mqtt_return_code = return_code; - state_test_data->error = error_code; - state_test_data->connection_completed = true; - aws_mutex_unlock(&state_test_data->lock); - - aws_condition_variable_notify_one(&state_test_data->cvar); -} - -static bool s_is_connection_completed(void *arg) { - struct mqtt_connection_state_test *state_test_data = arg; - return state_test_data->connection_completed; -} - -static void s_wait_for_connection_to_complete(struct mqtt_connection_state_test *state_test_data) { - aws_mutex_lock(&state_test_data->lock); - aws_condition_variable_wait_pred( - &state_test_data->cvar, &state_test_data->lock, s_is_connection_completed, state_test_data); - state_test_data->connection_completed = false; - aws_mutex_unlock(&state_test_data->lock); -} - -void s_on_disconnect_fn(struct aws_mqtt_client_connection *connection, void *userdata) { - (void)connection; - struct mqtt_connection_state_test *state_test_data = userdata; - AWS_LOGF_DEBUG(TEST_LOG_SUBJECT, "disconnect completed"); - aws_mutex_lock(&state_test_data->lock); - state_test_data->client_disconnect_completed = true; - aws_mutex_unlock(&state_test_data->lock); - - aws_condition_variable_notify_one(&state_test_data->cvar); -} - -static bool s_is_disconnect_completed(void *arg) { - struct mqtt_connection_state_test *state_test_data = arg; - return state_test_data->client_disconnect_completed && state_test_data->server_disconnect_completed; -} - -static void s_wait_for_disconnect_to_complete(struct mqtt_connection_state_test *state_test_data) { - aws_mutex_lock(&state_test_data->lock); - aws_condition_variable_wait_pred( - &state_test_data->cvar, &state_test_data->lock, s_is_disconnect_completed, state_test_data); - state_test_data->client_disconnect_completed = false; - state_test_data->server_disconnect_completed = false; - aws_mutex_unlock(&state_test_data->lock); -} - -static void s_on_any_publish_received( - struct aws_mqtt_client_connection *connection, - const struct aws_byte_cursor *topic, - const struct aws_byte_cursor *payload, - bool dup, - enum aws_mqtt_qos qos, - bool retain, - void *userdata) { - (void)connection; - struct mqtt_connection_state_test *state_test_data = userdata; - - struct aws_byte_buf payload_cp; - aws_byte_buf_init_copy_from_cursor(&payload_cp, state_test_data->allocator, *payload); - struct aws_byte_buf topic_cp; - aws_byte_buf_init_copy_from_cursor(&topic_cp, state_test_data->allocator, *topic); - struct received_publish_packet received_packet = { - .payload = payload_cp, - .topic = topic_cp, - .dup = dup, - .qos = qos, - .retain = retain, - }; - - aws_mutex_lock(&state_test_data->lock); - aws_array_list_push_back(&state_test_data->any_published_messages, &received_packet); - state_test_data->any_publishes_received++; - aws_mutex_unlock(&state_test_data->lock); - aws_condition_variable_notify_one(&state_test_data->cvar); -} - -static bool s_is_any_publish_received(void *arg) { - struct mqtt_connection_state_test *state_test_data = arg; - return state_test_data->any_publishes_received == state_test_data->expected_any_publishes; -} - -static void s_wait_for_any_publish(struct mqtt_connection_state_test *state_test_data) { - aws_mutex_lock(&state_test_data->lock); - aws_condition_variable_wait_pred( - &state_test_data->cvar, &state_test_data->lock, s_is_any_publish_received, state_test_data); - state_test_data->any_publishes_received = 0; - state_test_data->expected_any_publishes = 0; - aws_mutex_unlock(&state_test_data->lock); -} - -static void s_on_publish_received( - struct aws_mqtt_client_connection *connection, - const struct aws_byte_cursor *topic, - const struct aws_byte_cursor *payload, - bool dup, - enum aws_mqtt_qos qos, - bool retain, - void *userdata) { - - (void)connection; - (void)topic; - struct mqtt_connection_state_test *state_test_data = userdata; - - struct aws_byte_buf payload_cp; - aws_byte_buf_init_copy_from_cursor(&payload_cp, state_test_data->allocator, *payload); - struct aws_byte_buf topic_cp; - aws_byte_buf_init_copy_from_cursor(&topic_cp, state_test_data->allocator, *topic); - struct received_publish_packet received_packet = { - .payload = payload_cp, - .topic = topic_cp, - .dup = dup, - .qos = qos, - .retain = retain, - }; - - aws_mutex_lock(&state_test_data->lock); - aws_array_list_push_back(&state_test_data->published_messages, &received_packet); - state_test_data->publishes_received++; - aws_mutex_unlock(&state_test_data->lock); - aws_condition_variable_notify_one(&state_test_data->cvar); -} - -static bool s_is_publish_received(void *arg) { - struct mqtt_connection_state_test *state_test_data = arg; - return state_test_data->publishes_received == state_test_data->expected_publishes; -} - -static void s_wait_for_publish(struct mqtt_connection_state_test *state_test_data) { - aws_mutex_lock(&state_test_data->lock); - aws_condition_variable_wait_pred( - &state_test_data->cvar, &state_test_data->lock, s_is_publish_received, state_test_data); - state_test_data->publishes_received = 0; - state_test_data->expected_publishes = 0; - aws_mutex_unlock(&state_test_data->lock); -} - -static void s_on_suback( - struct aws_mqtt_client_connection *connection, - uint16_t packet_id, - const struct aws_byte_cursor *topic, - enum aws_mqtt_qos qos, - int error_code, - void *userdata) { - (void)connection; - (void)packet_id; - (void)topic; - - struct mqtt_connection_state_test *state_test_data = userdata; - - aws_mutex_lock(&state_test_data->lock); - if (!error_code) { - aws_array_list_push_back(&state_test_data->qos_returned, &qos); - } - state_test_data->subscribe_completed = true; - state_test_data->subscribe_complete_error = error_code; - aws_mutex_unlock(&state_test_data->lock); - aws_condition_variable_notify_one(&state_test_data->cvar); -} - -static bool s_is_subscribe_completed(void *arg) { - struct mqtt_connection_state_test *state_test_data = arg; - return state_test_data->subscribe_completed; -} - -static void s_wait_for_subscribe_to_complete(struct mqtt_connection_state_test *state_test_data) { - aws_mutex_lock(&state_test_data->lock); - aws_condition_variable_wait_pred( - &state_test_data->cvar, &state_test_data->lock, s_is_subscribe_completed, state_test_data); - state_test_data->subscribe_completed = false; - aws_mutex_unlock(&state_test_data->lock); -} - -static void s_on_multi_suback( - struct aws_mqtt_client_connection *connection, - uint16_t packet_id, - const struct aws_array_list *topic_subacks, /* contains aws_mqtt_topic_subscription pointers */ - int error_code, - void *userdata) { - (void)connection; - (void)packet_id; - (void)topic_subacks; - (void)error_code; - - struct mqtt_connection_state_test *state_test_data = userdata; - - aws_mutex_lock(&state_test_data->lock); - state_test_data->subscribe_completed = true; - if (!error_code) { - size_t length = aws_array_list_length(topic_subacks); - for (size_t i = 0; i < length; ++i) { - struct aws_mqtt_topic_subscription *subscription = NULL; - aws_array_list_get_at(topic_subacks, &subscription, i); - aws_array_list_push_back(&state_test_data->qos_returned, &subscription->qos); - } - } - aws_mutex_unlock(&state_test_data->lock); - aws_condition_variable_notify_one(&state_test_data->cvar); -} - -static void s_on_op_complete( - struct aws_mqtt_client_connection *connection, - uint16_t packet_id, - int error_code, - void *userdata) { - (void)connection; - (void)packet_id; - - struct mqtt_connection_state_test *state_test_data = userdata; - AWS_LOGF_DEBUG(TEST_LOG_SUBJECT, "pub op completed"); - aws_mutex_lock(&state_test_data->lock); - state_test_data->ops_completed++; - state_test_data->op_complete_error = error_code; - aws_mutex_unlock(&state_test_data->lock); - aws_condition_variable_notify_one(&state_test_data->cvar); -} - -static bool s_is_ops_completed(void *arg) { - struct mqtt_connection_state_test *state_test_data = arg; - return state_test_data->ops_completed == state_test_data->expected_ops_completed; -} - -static void s_wait_for_ops_completed(struct mqtt_connection_state_test *state_test_data) { - aws_mutex_lock(&state_test_data->lock); - aws_condition_variable_wait_for_pred( - &state_test_data->cvar, &state_test_data->lock, 10000000000, s_is_ops_completed, state_test_data); - aws_mutex_unlock(&state_test_data->lock); + return aws_test311_clean_up_mqtt_server_fn(allocator, setup_result, ctx); } /* @@ -673,14 +34,14 @@ static int s_test_mqtt_connect_disconnect_fn(struct aws_allocator *allocator, vo .client_id = aws_byte_cursor_from_c_str("client1234"), .host_name = aws_byte_cursor_from_c_str(state_test_data->endpoint.address), .socket_options = &state_test_data->socket_options, - .on_connection_complete = s_on_connection_complete_fn, + .on_connection_complete = aws_test311_on_connection_complete_fn, }; ASSERT_SUCCESS(aws_mqtt_client_connection_connect(state_test_data->mqtt_connection, &connection_options)); - s_wait_for_connection_to_complete(state_test_data); - ASSERT_SUCCESS( - aws_mqtt_client_connection_disconnect(state_test_data->mqtt_connection, s_on_disconnect_fn, state_test_data)); - s_wait_for_disconnect_to_complete(state_test_data); + aws_test311_wait_for_connection_to_complete(state_test_data); + ASSERT_SUCCESS(aws_mqtt_client_connection_disconnect( + state_test_data->mqtt_connection, aws_test311_on_disconnect_fn, state_test_data)); + aws_test311_wait_for_disconnect_to_complete(state_test_data); /* Decode all received packets by mock server */ ASSERT_SUCCESS(mqtt_mock_server_decode_packets(state_test_data->mock_server)); @@ -730,14 +91,14 @@ static int s_test_mqtt_connect_set_will_login_fn(struct aws_allocator *allocator .client_id = aws_byte_cursor_from_c_str("client1234"), .host_name = aws_byte_cursor_from_c_str(state_test_data->endpoint.address), .socket_options = &state_test_data->socket_options, - .on_connection_complete = s_on_connection_complete_fn, + .on_connection_complete = aws_test311_on_connection_complete_fn, }; ASSERT_SUCCESS(aws_mqtt_client_connection_connect(state_test_data->mqtt_connection, &connection_options)); - s_wait_for_connection_to_complete(state_test_data); - ASSERT_SUCCESS( - aws_mqtt_client_connection_disconnect(state_test_data->mqtt_connection, s_on_disconnect_fn, state_test_data)); - s_wait_for_disconnect_to_complete(state_test_data); + aws_test311_wait_for_connection_to_complete(state_test_data); + ASSERT_SUCCESS(aws_mqtt_client_connection_disconnect( + state_test_data->mqtt_connection, aws_test311_on_disconnect_fn, state_test_data)); + aws_test311_wait_for_disconnect_to_complete(state_test_data); /* Decode all received packets by mock server */ ASSERT_SUCCESS(mqtt_mock_server_decode_packets(state_test_data->mock_server)); @@ -766,7 +127,7 @@ static int s_test_mqtt_connect_set_will_login_fn(struct aws_allocator *allocator /* Connect to the mock server again. If set will&loggin message is not called before the next connect, the * will&loggin message will still be there and be sent to the server again */ ASSERT_SUCCESS(aws_mqtt_client_connection_connect(state_test_data->mqtt_connection, &connection_options)); - s_wait_for_connection_to_complete(state_test_data); + aws_test311_wait_for_connection_to_complete(state_test_data); ASSERT_SUCCESS(mqtt_mock_server_decode_packets(state_test_data->mock_server)); /* The second CONNECT packet */ @@ -784,9 +145,9 @@ static int s_test_mqtt_connect_set_will_login_fn(struct aws_allocator *allocator ASSERT_TRUE(aws_byte_cursor_eq(&received_packet->password, &password)); /* disconnect */ - ASSERT_SUCCESS( - aws_mqtt_client_connection_disconnect(state_test_data->mqtt_connection, s_on_disconnect_fn, state_test_data)); - s_wait_for_disconnect_to_complete(state_test_data); + ASSERT_SUCCESS(aws_mqtt_client_connection_disconnect( + state_test_data->mqtt_connection, aws_test311_on_disconnect_fn, state_test_data)); + aws_test311_wait_for_disconnect_to_complete(state_test_data); /* set new will & loggin message, before next connect, the next CONNECT packet will contain the new information */ struct aws_byte_cursor new_will_payload = aws_byte_cursor_from_c_str("this is a new will."); @@ -803,7 +164,7 @@ static int s_test_mqtt_connect_set_will_login_fn(struct aws_allocator *allocator /* connect again */ ASSERT_SUCCESS(aws_mqtt_client_connection_connect(state_test_data->mqtt_connection, &connection_options)); - s_wait_for_connection_to_complete(state_test_data); + aws_test311_wait_for_connection_to_complete(state_test_data); ASSERT_SUCCESS(mqtt_mock_server_decode_packets(state_test_data->mock_server)); /* The third CONNECT packet */ @@ -821,9 +182,9 @@ static int s_test_mqtt_connect_set_will_login_fn(struct aws_allocator *allocator ASSERT_TRUE(aws_byte_cursor_eq(&received_packet->password, &new_password)); /* disconnect. FINISHED */ - ASSERT_SUCCESS( - aws_mqtt_client_connection_disconnect(state_test_data->mqtt_connection, s_on_disconnect_fn, state_test_data)); - s_wait_for_disconnect_to_complete(state_test_data); + ASSERT_SUCCESS(aws_mqtt_client_connection_disconnect( + state_test_data->mqtt_connection, aws_test311_on_disconnect_fn, state_test_data)); + aws_test311_wait_for_disconnect_to_complete(state_test_data); return AWS_OP_SUCCESS; } @@ -852,14 +213,14 @@ static int s_test_mqtt_connection_interrupted_fn(struct aws_allocator *allocator .client_id = aws_byte_cursor_from_c_str("client1234"), .host_name = aws_byte_cursor_from_c_str(state_test_data->endpoint.address), .socket_options = &state_test_data->socket_options, - .on_connection_complete = s_on_connection_complete_fn, + .on_connection_complete = aws_test311_on_connection_complete_fn, }; aws_mqtt_client_connection_set_reconnect_timeout( state_test_data->mqtt_connection, MIN_RECONNECT_DELAY_SECONDS, MAX_RECONNECT_DELAY_SECONDS); ASSERT_SUCCESS(aws_mqtt_client_connection_connect(state_test_data->mqtt_connection, &connection_options)); - s_wait_for_connection_to_complete(state_test_data); + aws_test311_wait_for_connection_to_complete(state_test_data); /* shut it down and make sure the client automatically reconnects.*/ uint64_t now = 0; @@ -867,7 +228,7 @@ static int s_test_mqtt_connection_interrupted_fn(struct aws_allocator *allocator uint64_t start_shutdown = now; aws_channel_shutdown(state_test_data->server_channel, AWS_OP_SUCCESS); - s_wait_for_reconnect_to_complete(state_test_data); + aws_test311_wait_for_reconnect_to_complete(state_test_data); aws_high_res_clock_get_ticks(&now); uint64_t reconnect_complete = now; @@ -877,9 +238,9 @@ static int s_test_mqtt_connection_interrupted_fn(struct aws_allocator *allocator aws_timestamp_convert(elapsed_time, AWS_TIMESTAMP_NANOS, AWS_TIMESTAMP_SECS, NULL) >= MIN_RECONNECT_DELAY_SECONDS); - ASSERT_SUCCESS( - aws_mqtt_client_connection_disconnect(state_test_data->mqtt_connection, s_on_disconnect_fn, state_test_data)); - s_wait_for_disconnect_to_complete(state_test_data); + ASSERT_SUCCESS(aws_mqtt_client_connection_disconnect( + state_test_data->mqtt_connection, aws_test311_on_disconnect_fn, state_test_data)); + aws_test311_wait_for_disconnect_to_complete(state_test_data); /* Decode all received packets by mock server */ ASSERT_SUCCESS(mqtt_mock_server_decode_packets(state_test_data->mock_server)); @@ -922,21 +283,21 @@ static int s_test_mqtt_connection_timeout_fn(struct aws_allocator *allocator, vo .client_id = aws_byte_cursor_from_c_str("client1234"), .host_name = aws_byte_cursor_from_c_str(state_test_data->endpoint.address), .socket_options = &state_test_data->socket_options, - .on_connection_complete = s_on_connection_complete_fn, + .on_connection_complete = aws_test311_on_connection_complete_fn, .keep_alive_time_secs = DEFAULT_TEST_KEEP_ALIVE_S, .ping_timeout_ms = DEFAULT_TEST_PING_TIMEOUT_MS, }; mqtt_mock_server_set_max_ping_resp(state_test_data->mock_server, 0); ASSERT_SUCCESS(aws_mqtt_client_connection_connect(state_test_data->mqtt_connection, &connection_options)); - s_wait_for_connection_to_complete(state_test_data); + aws_test311_wait_for_connection_to_complete(state_test_data); /* this should take about 1.1 seconds for the timeout and reconnect.*/ - s_wait_for_reconnect_to_complete(state_test_data); + aws_test311_wait_for_reconnect_to_complete(state_test_data); - ASSERT_SUCCESS( - aws_mqtt_client_connection_disconnect(state_test_data->mqtt_connection, s_on_disconnect_fn, state_test_data)); - s_wait_for_disconnect_to_complete(state_test_data); + ASSERT_SUCCESS(aws_mqtt_client_connection_disconnect( + state_test_data->mqtt_connection, aws_test311_on_disconnect_fn, state_test_data)); + aws_test311_wait_for_disconnect_to_complete(state_test_data); ASSERT_INT_EQUALS(AWS_ERROR_MQTT_TIMEOUT, state_test_data->interruption_error); @@ -962,14 +323,14 @@ static int s_test_mqtt_connection_any_publish_fn(struct aws_allocator *allocator .client_id = aws_byte_cursor_from_c_str("client1234"), .host_name = aws_byte_cursor_from_c_str(state_test_data->endpoint.address), .socket_options = &state_test_data->socket_options, - .on_connection_complete = s_on_connection_complete_fn, + .on_connection_complete = aws_test311_on_connection_complete_fn, }; struct aws_byte_cursor topic_1 = aws_byte_cursor_from_c_str("/test/topic1"); struct aws_byte_cursor topic_2 = aws_byte_cursor_from_c_str("/test/topic2"); ASSERT_SUCCESS(aws_mqtt_client_connection_connect(state_test_data->mqtt_connection, &connection_options)); - s_wait_for_connection_to_complete(state_test_data); + aws_test311_wait_for_connection_to_complete(state_test_data); /* NOTE: mock server sends to client with no subscription at all, which should not happen in the real world! */ state_test_data->expected_any_publishes = 2; @@ -990,12 +351,12 @@ static int s_test_mqtt_connection_any_publish_fn(struct aws_allocator *allocator AWS_MQTT_QOS_AT_LEAST_ONCE, false /*retain*/)); - s_wait_for_any_publish(state_test_data); + aws_test311_wait_for_any_publish(state_test_data); mqtt_mock_server_wait_for_pubacks(state_test_data->mock_server, 2); - ASSERT_SUCCESS( - aws_mqtt_client_connection_disconnect(state_test_data->mqtt_connection, s_on_disconnect_fn, state_test_data)); - s_wait_for_disconnect_to_complete(state_test_data); + ASSERT_SUCCESS(aws_mqtt_client_connection_disconnect( + state_test_data->mqtt_connection, aws_test311_on_disconnect_fn, state_test_data)); + aws_test311_wait_for_disconnect_to_complete(state_test_data); /* Decode all received packets by mock server */ ASSERT_SUCCESS(mqtt_mock_server_decode_packets(state_test_data->mock_server)); @@ -1048,14 +409,14 @@ static int s_test_mqtt_connection_connack_timeout_fn(struct aws_allocator *alloc .client_id = aws_byte_cursor_from_c_str("client1234"), .host_name = aws_byte_cursor_from_c_str(state_test_data->endpoint.address), .socket_options = &state_test_data->socket_options, - .on_connection_complete = s_on_connection_complete_fn, + .on_connection_complete = aws_test311_on_connection_complete_fn, .keep_alive_time_secs = DEFAULT_TEST_KEEP_ALIVE_S, .ping_timeout_ms = DEFAULT_TEST_PING_TIMEOUT_MS, }; mqtt_mock_server_set_max_connack(state_test_data->mock_server, 0); ASSERT_SUCCESS(aws_mqtt_client_connection_connect(state_test_data->mqtt_connection, &connection_options)); - s_wait_for_connection_to_complete(state_test_data); + aws_test311_wait_for_connection_to_complete(state_test_data); ASSERT_INT_EQUALS(AWS_ERROR_MQTT_TIMEOUT, state_test_data->error); @@ -1080,14 +441,14 @@ static int s_test_mqtt_connection_failure_callback_fn(struct aws_allocator *allo .client_id = aws_byte_cursor_from_c_str("client1234"), .host_name = aws_byte_cursor_from_c_str(state_test_data->endpoint.address), .socket_options = &state_test_data->socket_options, - .on_connection_complete = s_on_connection_complete_fn, + .on_connection_complete = aws_test311_on_connection_complete_fn, .keep_alive_time_secs = DEFAULT_TEST_KEEP_ALIVE_S, .ping_timeout_ms = DEFAULT_TEST_PING_TIMEOUT_MS, }; mqtt_mock_server_set_max_connack(state_test_data->mock_server, 0); ASSERT_SUCCESS(aws_mqtt_client_connection_connect(state_test_data->mqtt_connection, &connection_options)); - s_wait_for_connection_to_fail(state_test_data); + aws_test311_wait_for_connection_to_fail(state_test_data); ASSERT_INT_EQUALS(AWS_ERROR_MQTT_TIMEOUT, state_test_data->error); @@ -1112,11 +473,11 @@ static int s_test_mqtt_connection_success_callback_fn(struct aws_allocator *allo .client_id = aws_byte_cursor_from_c_str("client1234"), .host_name = aws_byte_cursor_from_c_str(state_test_data->endpoint.address), .socket_options = &state_test_data->socket_options, - .on_connection_complete = s_on_connection_complete_fn, + .on_connection_complete = aws_test311_on_connection_complete_fn, }; ASSERT_SUCCESS(aws_mqtt_client_connection_connect(state_test_data->mqtt_connection, &connection_options)); - s_wait_for_connection_to_succeed(state_test_data); + aws_test311_wait_for_connection_to_succeed(state_test_data); /* Decode all received packets by mock server */ ASSERT_SUCCESS(mqtt_mock_server_decode_packets(state_test_data->mock_server)); @@ -1129,9 +490,9 @@ static int s_test_mqtt_connection_success_callback_fn(struct aws_allocator *allo ASSERT_TRUE(aws_byte_cursor_eq(&received_packet->client_identifier, &connection_options.client_id)); // Disconnect and finish - ASSERT_SUCCESS( - aws_mqtt_client_connection_disconnect(state_test_data->mqtt_connection, s_on_disconnect_fn, state_test_data)); - s_wait_for_disconnect_to_complete(state_test_data); + ASSERT_SUCCESS(aws_mqtt_client_connection_disconnect( + state_test_data->mqtt_connection, aws_test311_on_disconnect_fn, state_test_data)); + aws_test311_wait_for_disconnect_to_complete(state_test_data); return AWS_OP_SUCCESS; } @@ -1155,7 +516,7 @@ static int s_test_mqtt_subscribe_fn(struct aws_allocator *allocator, void *ctx) .client_id = aws_byte_cursor_from_c_str("client1234"), .host_name = aws_byte_cursor_from_c_str(state_test_data->endpoint.address), .socket_options = &state_test_data->socket_options, - .on_connection_complete = s_on_connection_complete_fn, + .on_connection_complete = aws_test311_on_connection_complete_fn, }; struct aws_byte_cursor sub_topic = aws_byte_cursor_from_c_str("/test/topic"); @@ -1164,17 +525,17 @@ static int s_test_mqtt_subscribe_fn(struct aws_allocator *allocator, void *ctx) state_test_data->mqtt_connection, &sub_topic, AWS_MQTT_QOS_AT_LEAST_ONCE, - s_on_publish_received, + aws_test311_on_publish_received, state_test_data, NULL, - s_on_suback, + aws_test311_on_suback, state_test_data); ASSERT_TRUE(packet_id > 0); ASSERT_SUCCESS(aws_mqtt_client_connection_connect(state_test_data->mqtt_connection, &connection_options)); - s_wait_for_connection_to_complete(state_test_data); + aws_test311_wait_for_connection_to_complete(state_test_data); - s_wait_for_subscribe_to_complete(state_test_data); + aws_test311_wait_for_subscribe_to_complete(state_test_data); state_test_data->expected_publishes = 2; state_test_data->expected_any_publishes = 2; @@ -1195,13 +556,13 @@ static int s_test_mqtt_subscribe_fn(struct aws_allocator *allocator, void *ctx) AWS_MQTT_QOS_AT_LEAST_ONCE, false /*retain*/)); - s_wait_for_publish(state_test_data); - s_wait_for_any_publish(state_test_data); + aws_test311_wait_for_publish(state_test_data); + aws_test311_wait_for_any_publish(state_test_data); mqtt_mock_server_wait_for_pubacks(state_test_data->mock_server, 2); - ASSERT_SUCCESS( - aws_mqtt_client_connection_disconnect(state_test_data->mqtt_connection, s_on_disconnect_fn, state_test_data)); - s_wait_for_disconnect_to_complete(state_test_data); + ASSERT_SUCCESS(aws_mqtt_client_connection_disconnect( + state_test_data->mqtt_connection, aws_test311_on_disconnect_fn, state_test_data)); + aws_test311_wait_for_disconnect_to_complete(state_test_data); /* Decode all received packets by mock server */ ASSERT_SUCCESS(mqtt_mock_server_decode_packets(state_test_data->mock_server)); @@ -1282,7 +643,7 @@ static int s_test_mqtt_subscribe_incoming_dup_fn(struct aws_allocator *allocator .client_id = aws_byte_cursor_from_c_str("client1234"), .host_name = aws_byte_cursor_from_c_str(state_test_data->endpoint.address), .socket_options = &state_test_data->socket_options, - .on_connection_complete = s_on_connection_complete_fn, + .on_connection_complete = aws_test311_on_connection_complete_fn, }; struct aws_byte_cursor subscribed_topic = aws_byte_cursor_from_c_str("/test/topic"); @@ -1292,16 +653,16 @@ static int s_test_mqtt_subscribe_incoming_dup_fn(struct aws_allocator *allocator state_test_data->mqtt_connection, &subscribed_topic, AWS_MQTT_QOS_AT_LEAST_ONCE, - s_on_publish_received, + aws_test311_on_publish_received, state_test_data, NULL, - s_on_suback, + aws_test311_on_suback, state_test_data); ASSERT_TRUE(packet_id > 0); ASSERT_SUCCESS(aws_mqtt_client_connection_connect(state_test_data->mqtt_connection, &connection_options)); - s_wait_for_connection_to_complete(state_test_data); - s_wait_for_subscribe_to_complete(state_test_data); + aws_test311_wait_for_connection_to_complete(state_test_data); + aws_test311_wait_for_subscribe_to_complete(state_test_data); state_test_data->expected_publishes = 4; state_test_data->expected_any_publishes = 8; @@ -1330,13 +691,13 @@ static int s_test_mqtt_subscribe_incoming_dup_fn(struct aws_allocator *allocator false /*retain*/)); } - s_wait_for_publish(state_test_data); - s_wait_for_any_publish(state_test_data); + aws_test311_wait_for_publish(state_test_data); + aws_test311_wait_for_any_publish(state_test_data); mqtt_mock_server_wait_for_pubacks(state_test_data->mock_server, 8); - ASSERT_SUCCESS( - aws_mqtt_client_connection_disconnect(state_test_data->mqtt_connection, s_on_disconnect_fn, state_test_data)); - s_wait_for_disconnect_to_complete(state_test_data); + ASSERT_SUCCESS(aws_mqtt_client_connection_disconnect( + state_test_data->mqtt_connection, aws_test311_on_disconnect_fn, state_test_data)); + aws_test311_wait_for_disconnect_to_complete(state_test_data); /* Decode all received packets by mock server */ ASSERT_SUCCESS(mqtt_mock_server_decode_packets(state_test_data->mock_server)); @@ -1419,11 +780,11 @@ static int s_test_mqtt_connect_subscribe_fail_from_broker_fn(struct aws_allocato .client_id = aws_byte_cursor_from_c_str("client1234"), .host_name = aws_byte_cursor_from_c_str(state_test_data->endpoint.address), .socket_options = &state_test_data->socket_options, - .on_connection_complete = s_on_connection_complete_fn, + .on_connection_complete = aws_test311_on_connection_complete_fn, }; ASSERT_SUCCESS(aws_mqtt_client_connection_connect(state_test_data->mqtt_connection, &connection_options)); - s_wait_for_connection_to_complete(state_test_data); + aws_test311_wait_for_connection_to_complete(state_test_data); /* Disable the auto ACK packets sent by the server, we will send failure SUBACK */ mqtt_mock_server_disable_auto_ack(state_test_data->mock_server); @@ -1434,15 +795,15 @@ static int s_test_mqtt_connect_subscribe_fail_from_broker_fn(struct aws_allocato state_test_data->mqtt_connection, &sub_topic, AWS_MQTT_QOS_AT_LEAST_ONCE, - s_on_publish_received, + aws_test311_on_publish_received, state_test_data, NULL, - s_on_suback, + aws_test311_on_suback, state_test_data); ASSERT_TRUE(packet_id > 0); ASSERT_SUCCESS(mqtt_mock_server_send_single_suback(state_test_data->mock_server, packet_id, AWS_MQTT_QOS_FAILURE)); - s_wait_for_subscribe_to_complete(state_test_data); + aws_test311_wait_for_subscribe_to_complete(state_test_data); /* Check the subscribe returned QoS is failure */ size_t length = aws_array_list_length(&state_test_data->qos_returned); ASSERT_UINT_EQUALS(1, length); @@ -1450,9 +811,9 @@ static int s_test_mqtt_connect_subscribe_fail_from_broker_fn(struct aws_allocato ASSERT_SUCCESS(aws_array_list_get_at(&state_test_data->qos_returned, &qos, 0)); ASSERT_UINT_EQUALS(AWS_MQTT_QOS_FAILURE, qos); - ASSERT_SUCCESS( - aws_mqtt_client_connection_disconnect(state_test_data->mqtt_connection, s_on_disconnect_fn, state_test_data)); - s_wait_for_disconnect_to_complete(state_test_data); + ASSERT_SUCCESS(aws_mqtt_client_connection_disconnect( + state_test_data->mqtt_connection, aws_test311_on_disconnect_fn, state_test_data)); + aws_test311_wait_for_disconnect_to_complete(state_test_data); return AWS_OP_SUCCESS; } @@ -1476,7 +837,7 @@ static int s_test_mqtt_subscribe_multi_fn(struct aws_allocator *allocator, void .client_id = aws_byte_cursor_from_c_str("client1234"), .host_name = aws_byte_cursor_from_c_str(state_test_data->endpoint.address), .socket_options = &state_test_data->socket_options, - .on_connection_complete = s_on_connection_complete_fn, + .on_connection_complete = aws_test311_on_connection_complete_fn, }; struct aws_byte_cursor sub_topic_1 = aws_byte_cursor_from_c_str("/test/topic1"); @@ -1485,14 +846,14 @@ static int s_test_mqtt_subscribe_multi_fn(struct aws_allocator *allocator, void struct aws_mqtt_topic_subscription sub1 = { .topic = sub_topic_1, .qos = AWS_MQTT_QOS_AT_LEAST_ONCE, - .on_publish = s_on_publish_received, + .on_publish = aws_test311_on_publish_received, .on_cleanup = NULL, .on_publish_ud = state_test_data, }; struct aws_mqtt_topic_subscription sub2 = { .topic = sub_topic_2, .qos = AWS_MQTT_QOS_AT_LEAST_ONCE, - .on_publish = s_on_publish_received, + .on_publish = aws_test311_on_publish_received, .on_cleanup = NULL, .on_publish_ud = state_test_data, }; @@ -1506,13 +867,13 @@ static int s_test_mqtt_subscribe_multi_fn(struct aws_allocator *allocator, void aws_array_list_push_back(&topic_filters, &sub2); uint16_t packet_id = aws_mqtt_client_connection_subscribe_multiple( - state_test_data->mqtt_connection, &topic_filters, s_on_multi_suback, state_test_data); + state_test_data->mqtt_connection, &topic_filters, aws_test311_on_multi_suback, state_test_data); ASSERT_TRUE(packet_id > 0); ASSERT_SUCCESS(aws_mqtt_client_connection_connect(state_test_data->mqtt_connection, &connection_options)); - s_wait_for_connection_to_complete(state_test_data); + aws_test311_wait_for_connection_to_complete(state_test_data); - s_wait_for_subscribe_to_complete(state_test_data); + aws_test311_wait_for_subscribe_to_complete(state_test_data); /* Check the subscribe returned QoS is expected */ size_t length = aws_array_list_length(&state_test_data->qos_returned); ASSERT_UINT_EQUALS(2, length); @@ -1539,7 +900,7 @@ static int s_test_mqtt_subscribe_multi_fn(struct aws_allocator *allocator, void false /*dup*/, AWS_MQTT_QOS_AT_LEAST_ONCE, false /*retain*/)); - s_wait_for_publish(state_test_data); + aws_test311_wait_for_publish(state_test_data); /* Let's do another publish on a topic that is not subscribed by client. * This can happen if the Server automatically assigned a subscription to the Client */ @@ -1553,13 +914,13 @@ static int s_test_mqtt_subscribe_multi_fn(struct aws_allocator *allocator, void false /*dup*/, AWS_MQTT_QOS_AT_LEAST_ONCE, false /*retain*/)); - s_wait_for_any_publish(state_test_data); + aws_test311_wait_for_any_publish(state_test_data); mqtt_mock_server_wait_for_pubacks(state_test_data->mock_server, 3); - ASSERT_SUCCESS( - aws_mqtt_client_connection_disconnect(state_test_data->mqtt_connection, s_on_disconnect_fn, state_test_data)); - s_wait_for_disconnect_to_complete(state_test_data); + ASSERT_SUCCESS(aws_mqtt_client_connection_disconnect( + state_test_data->mqtt_connection, aws_test311_on_disconnect_fn, state_test_data)); + aws_test311_wait_for_disconnect_to_complete(state_test_data); /* Decode all received packets by mock server */ ASSERT_SUCCESS(mqtt_mock_server_decode_packets(state_test_data->mock_server)); @@ -1627,7 +988,7 @@ static int s_test_mqtt_unsubscribe_fn(struct aws_allocator *allocator, void *ctx .client_id = aws_byte_cursor_from_c_str("client1234"), .host_name = aws_byte_cursor_from_c_str(state_test_data->endpoint.address), .socket_options = &state_test_data->socket_options, - .on_connection_complete = s_on_connection_complete_fn, + .on_connection_complete = aws_test311_on_connection_complete_fn, }; struct aws_byte_cursor sub_topic_1 = aws_byte_cursor_from_c_str("/test/topic1"); @@ -1636,14 +997,14 @@ static int s_test_mqtt_unsubscribe_fn(struct aws_allocator *allocator, void *ctx struct aws_mqtt_topic_subscription sub1 = { .topic = sub_topic_1, .qos = AWS_MQTT_QOS_AT_LEAST_ONCE, - .on_publish = s_on_publish_received, + .on_publish = aws_test311_on_publish_received, .on_cleanup = NULL, .on_publish_ud = state_test_data, }; struct aws_mqtt_topic_subscription sub2 = { .topic = sub_topic_2, .qos = AWS_MQTT_QOS_AT_LEAST_ONCE, - .on_publish = s_on_publish_received, + .on_publish = aws_test311_on_publish_received, .on_cleanup = NULL, .on_publish_ud = state_test_data, }; @@ -1657,13 +1018,13 @@ static int s_test_mqtt_unsubscribe_fn(struct aws_allocator *allocator, void *ctx aws_array_list_push_back(&topic_filters, &sub2); uint16_t sub_packet_id = aws_mqtt_client_connection_subscribe_multiple( - state_test_data->mqtt_connection, &topic_filters, s_on_multi_suback, state_test_data); + state_test_data->mqtt_connection, &topic_filters, aws_test311_on_multi_suback, state_test_data); ASSERT_TRUE(sub_packet_id > 0); ASSERT_SUCCESS(aws_mqtt_client_connection_connect(state_test_data->mqtt_connection, &connection_options)); - s_wait_for_connection_to_complete(state_test_data); + aws_test311_wait_for_connection_to_complete(state_test_data); - s_wait_for_subscribe_to_complete(state_test_data); + aws_test311_wait_for_subscribe_to_complete(state_test_data); state_test_data->expected_any_publishes = 2; struct aws_byte_cursor payload_1 = aws_byte_cursor_from_c_str("Test Message 1"); @@ -1682,7 +1043,7 @@ static int s_test_mqtt_unsubscribe_fn(struct aws_allocator *allocator, void *ctx false /*dup*/, AWS_MQTT_QOS_AT_LEAST_ONCE, false /*retain*/)); - s_wait_for_any_publish(state_test_data); + aws_test311_wait_for_any_publish(state_test_data); mqtt_mock_server_wait_for_pubacks(state_test_data->mock_server, 2); aws_mutex_lock(&state_test_data->lock); @@ -1690,7 +1051,7 @@ static int s_test_mqtt_unsubscribe_fn(struct aws_allocator *allocator, void *ctx aws_mutex_unlock(&state_test_data->lock); /* unsubscribe to the first topic */ uint16_t unsub_packet_id = aws_mqtt_client_connection_unsubscribe( - state_test_data->mqtt_connection, &sub_topic_1, s_on_op_complete, state_test_data); + state_test_data->mqtt_connection, &sub_topic_1, aws_test311_on_op_complete, state_test_data); ASSERT_TRUE(unsub_packet_id > 0); /* Even when the UNSUBACK has not received, the client will not invoke the on_pub callback for that topic */ ASSERT_SUCCESS(mqtt_mock_server_send_publish( @@ -1708,13 +1069,13 @@ static int s_test_mqtt_unsubscribe_fn(struct aws_allocator *allocator, void *ctx AWS_MQTT_QOS_AT_LEAST_ONCE, false /*retain*/)); state_test_data->expected_any_publishes = 2; - s_wait_for_any_publish(state_test_data); + aws_test311_wait_for_any_publish(state_test_data); mqtt_mock_server_wait_for_pubacks(state_test_data->mock_server, 2); - s_wait_for_ops_completed(state_test_data); + aws_test311_wait_for_ops_completed(state_test_data); - ASSERT_SUCCESS( - aws_mqtt_client_connection_disconnect(state_test_data->mqtt_connection, s_on_disconnect_fn, state_test_data)); - s_wait_for_disconnect_to_complete(state_test_data); + ASSERT_SUCCESS(aws_mqtt_client_connection_disconnect( + state_test_data->mqtt_connection, aws_test311_on_disconnect_fn, state_test_data)); + aws_test311_wait_for_disconnect_to_complete(state_test_data); /* Decode all received packets by mock server */ ASSERT_SUCCESS(mqtt_mock_server_decode_packets(state_test_data->mock_server)); @@ -1797,7 +1158,7 @@ static int s_test_mqtt_resubscribe_fn(struct aws_allocator *allocator, void *ctx .client_id = aws_byte_cursor_from_c_str("client1234"), .host_name = aws_byte_cursor_from_c_str(state_test_data->endpoint.address), .socket_options = &state_test_data->socket_options, - .on_connection_complete = s_on_connection_complete_fn, + .on_connection_complete = aws_test311_on_connection_complete_fn, }; struct aws_byte_cursor sub_topic_1 = aws_byte_cursor_from_c_str("/test/topic1"); @@ -1807,21 +1168,21 @@ static int s_test_mqtt_resubscribe_fn(struct aws_allocator *allocator, void *ctx struct aws_mqtt_topic_subscription sub1 = { .topic = sub_topic_1, .qos = AWS_MQTT_QOS_AT_LEAST_ONCE, - .on_publish = s_on_publish_received, + .on_publish = aws_test311_on_publish_received, .on_cleanup = NULL, .on_publish_ud = state_test_data, }; struct aws_mqtt_topic_subscription sub2 = { .topic = sub_topic_2, .qos = AWS_MQTT_QOS_AT_LEAST_ONCE, - .on_publish = s_on_publish_received, + .on_publish = aws_test311_on_publish_received, .on_cleanup = NULL, .on_publish_ud = state_test_data, }; struct aws_mqtt_topic_subscription sub3 = { .topic = sub_topic_3, .qos = AWS_MQTT_QOS_AT_LEAST_ONCE, - .on_publish = s_on_publish_received, + .on_publish = aws_test311_on_publish_received, .on_cleanup = NULL, .on_publish_ud = state_test_data, }; @@ -1836,31 +1197,31 @@ static int s_test_mqtt_resubscribe_fn(struct aws_allocator *allocator, void *ctx aws_array_list_push_back(&topic_filters, &sub3); uint16_t sub_packet_id = aws_mqtt_client_connection_subscribe_multiple( - state_test_data->mqtt_connection, &topic_filters, s_on_multi_suback, state_test_data); + state_test_data->mqtt_connection, &topic_filters, aws_test311_on_multi_suback, state_test_data); ASSERT_TRUE(sub_packet_id > 0); ASSERT_SUCCESS(aws_mqtt_client_connection_connect(state_test_data->mqtt_connection, &connection_options)); - s_wait_for_connection_to_complete(state_test_data); + aws_test311_wait_for_connection_to_complete(state_test_data); - s_wait_for_subscribe_to_complete(state_test_data); + aws_test311_wait_for_subscribe_to_complete(state_test_data); aws_mutex_lock(&state_test_data->lock); state_test_data->expected_ops_completed = 1; aws_mutex_unlock(&state_test_data->lock); /* unsubscribe to the first topic */ uint16_t unsub_packet_id = aws_mqtt_client_connection_unsubscribe( - state_test_data->mqtt_connection, &sub_topic_1, s_on_op_complete, state_test_data); + state_test_data->mqtt_connection, &sub_topic_1, aws_test311_on_op_complete, state_test_data); ASSERT_TRUE(unsub_packet_id > 0); - s_wait_for_ops_completed(state_test_data); + aws_test311_wait_for_ops_completed(state_test_data); /* client still subscribes to topic_2 & topic_3 */ - ASSERT_SUCCESS( - aws_mqtt_client_connection_disconnect(state_test_data->mqtt_connection, s_on_disconnect_fn, state_test_data)); - s_wait_for_disconnect_to_complete(state_test_data); + ASSERT_SUCCESS(aws_mqtt_client_connection_disconnect( + state_test_data->mqtt_connection, aws_test311_on_disconnect_fn, state_test_data)); + aws_test311_wait_for_disconnect_to_complete(state_test_data); /* reconnection to the same server */ ASSERT_SUCCESS(aws_mqtt_client_connection_connect(state_test_data->mqtt_connection, &connection_options)); - s_wait_for_connection_to_complete(state_test_data); + aws_test311_wait_for_connection_to_complete(state_test_data); /* Get all the packets out of the way */ ASSERT_SUCCESS(mqtt_mock_server_decode_packets(state_test_data->mock_server)); @@ -1870,14 +1231,14 @@ static int s_test_mqtt_resubscribe_fn(struct aws_allocator *allocator, void *ctx ASSERT_UINT_EQUALS(AWS_MQTT_PACKET_CONNECT, t_received_packet->type); /* resubscribes to topic_2 & topic_3 */ - uint16_t resub_packet_id = - aws_mqtt_resubscribe_existing_topics(state_test_data->mqtt_connection, s_on_multi_suback, state_test_data); + uint16_t resub_packet_id = aws_mqtt_resubscribe_existing_topics( + state_test_data->mqtt_connection, aws_test311_on_multi_suback, state_test_data); ASSERT_TRUE(resub_packet_id > 0); - s_wait_for_subscribe_to_complete(state_test_data); + aws_test311_wait_for_subscribe_to_complete(state_test_data); - ASSERT_SUCCESS( - aws_mqtt_client_connection_disconnect(state_test_data->mqtt_connection, s_on_disconnect_fn, state_test_data)); - s_wait_for_disconnect_to_complete(state_test_data); + ASSERT_SUCCESS(aws_mqtt_client_connection_disconnect( + state_test_data->mqtt_connection, aws_test311_on_disconnect_fn, state_test_data)); + aws_test311_wait_for_disconnect_to_complete(state_test_data); ASSERT_SUCCESS(mqtt_mock_server_decode_packets(state_test_data->mock_server)); struct mqtt_decoded_packet *received_packet = @@ -1915,7 +1276,7 @@ static int s_test_mqtt_publish_fn(struct aws_allocator *allocator, void *ctx) { .client_id = aws_byte_cursor_from_c_str("client1234"), .host_name = aws_byte_cursor_from_c_str(state_test_data->endpoint.address), .socket_options = &state_test_data->socket_options, - .on_connection_complete = s_on_connection_complete_fn, + .on_connection_complete = aws_test311_on_connection_complete_fn, }; struct aws_byte_cursor pub_topic = aws_byte_cursor_from_c_str("/test/topic"); @@ -1923,7 +1284,7 @@ static int s_test_mqtt_publish_fn(struct aws_allocator *allocator, void *ctx) { struct aws_byte_cursor payload_2 = aws_byte_cursor_from_c_str("Test Message 2"); ASSERT_SUCCESS(aws_mqtt_client_connection_connect(state_test_data->mqtt_connection, &connection_options)); - s_wait_for_connection_to_complete(state_test_data); + aws_test311_wait_for_connection_to_complete(state_test_data); aws_mutex_lock(&state_test_data->lock); state_test_data->expected_ops_completed = 3; @@ -1934,7 +1295,7 @@ static int s_test_mqtt_publish_fn(struct aws_allocator *allocator, void *ctx) { AWS_MQTT_QOS_AT_LEAST_ONCE, false, &payload_1, - s_on_op_complete, + aws_test311_on_op_complete, state_test_data); ASSERT_TRUE(packet_id_1 > 0); uint16_t packet_id_2 = aws_mqtt_client_connection_publish( @@ -1943,7 +1304,7 @@ static int s_test_mqtt_publish_fn(struct aws_allocator *allocator, void *ctx) { AWS_MQTT_QOS_AT_LEAST_ONCE, false, &payload_2, - s_on_op_complete, + aws_test311_on_op_complete, state_test_data); ASSERT_TRUE(packet_id_2 > 0); @@ -1954,15 +1315,15 @@ static int s_test_mqtt_publish_fn(struct aws_allocator *allocator, void *ctx) { AWS_MQTT_QOS_AT_LEAST_ONCE, false, NULL, - s_on_op_complete, + aws_test311_on_op_complete, state_test_data); ASSERT_TRUE(packet_id_3 > 0); - s_wait_for_ops_completed(state_test_data); + aws_test311_wait_for_ops_completed(state_test_data); - ASSERT_SUCCESS( - aws_mqtt_client_connection_disconnect(state_test_data->mqtt_connection, s_on_disconnect_fn, state_test_data)); - s_wait_for_disconnect_to_complete(state_test_data); + ASSERT_SUCCESS(aws_mqtt_client_connection_disconnect( + state_test_data->mqtt_connection, aws_test311_on_disconnect_fn, state_test_data)); + aws_test311_wait_for_disconnect_to_complete(state_test_data); /* Decode all received packets by mock server */ ASSERT_SUCCESS(mqtt_mock_server_decode_packets(state_test_data->mock_server)); @@ -2012,7 +1373,7 @@ static int s_test_mqtt_publish_payload_fn(struct aws_allocator *allocator, void .client_id = aws_byte_cursor_from_c_str("client1234"), .host_name = aws_byte_cursor_from_c_str(state_test_data->endpoint.address), .socket_options = &state_test_data->socket_options, - .on_connection_complete = s_on_connection_complete_fn, + .on_connection_complete = aws_test311_on_connection_complete_fn, }; struct aws_byte_cursor pub_topic = aws_byte_cursor_from_c_str("/test/topic"); @@ -2022,7 +1383,7 @@ static int s_test_mqtt_publish_payload_fn(struct aws_allocator *allocator, void struct aws_byte_cursor payload_curser = aws_byte_cursor_from_buf(&buf_payload); ASSERT_SUCCESS(aws_mqtt_client_connection_connect(state_test_data->mqtt_connection, &connection_options)); - s_wait_for_connection_to_complete(state_test_data); + aws_test311_wait_for_connection_to_complete(state_test_data); aws_mutex_lock(&state_test_data->lock); state_test_data->expected_ops_completed = 1; @@ -2033,17 +1394,17 @@ static int s_test_mqtt_publish_payload_fn(struct aws_allocator *allocator, void AWS_MQTT_QOS_AT_LEAST_ONCE, false, &payload_curser, - s_on_op_complete, + aws_test311_on_op_complete, state_test_data); ASSERT_TRUE(packet_id > 0); /* clean up the payload buf, as user don't need to manage the buf and keep it valid until publish completes */ aws_byte_buf_clean_up(&buf_payload); - s_wait_for_ops_completed(state_test_data); + aws_test311_wait_for_ops_completed(state_test_data); - ASSERT_SUCCESS( - aws_mqtt_client_connection_disconnect(state_test_data->mqtt_connection, s_on_disconnect_fn, state_test_data)); - s_wait_for_disconnect_to_complete(state_test_data); + ASSERT_SUCCESS(aws_mqtt_client_connection_disconnect( + state_test_data->mqtt_connection, aws_test311_on_disconnect_fn, state_test_data)); + aws_test311_wait_for_disconnect_to_complete(state_test_data); /* Decode all received packets by mock server */ ASSERT_SUCCESS(mqtt_mock_server_decode_packets(state_test_data->mock_server)); @@ -2087,19 +1448,19 @@ static int s_test_mqtt_connection_offline_publish_fn(struct aws_allocator *alloc .client_id = aws_byte_cursor_from_c_str("client1234"), .host_name = aws_byte_cursor_from_c_str(state_test_data->endpoint.address), .socket_options = &state_test_data->socket_options, - .on_connection_complete = s_on_connection_complete_fn, + .on_connection_complete = aws_test311_on_connection_complete_fn, .ping_timeout_ms = DEFAULT_TEST_PING_TIMEOUT_MS, .keep_alive_time_secs = DEFAULT_TEST_KEEP_ALIVE_S, }; ASSERT_SUCCESS(aws_mqtt_client_connection_connect(state_test_data->mqtt_connection, &connection_options)); - s_wait_for_connection_to_complete(state_test_data); + aws_test311_wait_for_connection_to_complete(state_test_data); mqtt_mock_server_set_max_connack(state_test_data->mock_server, 0); /* shut it down and make sure the client automatically reconnects.*/ aws_channel_shutdown(state_test_data->server_channel, AWS_OP_SUCCESS); - s_wait_for_interrupt_to_complete(state_test_data); + aws_test311_wait_for_interrupt_to_complete(state_test_data); state_test_data->server_disconnect_completed = false; @@ -2118,7 +1479,7 @@ static int s_test_mqtt_connection_offline_publish_fn(struct aws_allocator *alloc AWS_MQTT_QOS_AT_LEAST_ONCE, false, &payload_1, - s_on_op_complete, + aws_test311_on_op_complete, state_test_data) > 0); ASSERT_TRUE( aws_mqtt_client_connection_publish( @@ -2127,21 +1488,21 @@ static int s_test_mqtt_connection_offline_publish_fn(struct aws_allocator *alloc AWS_MQTT_QOS_AT_LEAST_ONCE, false, &payload_2, - s_on_op_complete, + aws_test311_on_op_complete, state_test_data) > 0); aws_mutex_lock(&state_test_data->lock); ASSERT_FALSE(state_test_data->connection_resumed); aws_mutex_unlock(&state_test_data->lock); mqtt_mock_server_set_max_connack(state_test_data->mock_server, SIZE_MAX); - s_wait_for_ops_completed(state_test_data); + aws_test311_wait_for_ops_completed(state_test_data); aws_mutex_lock(&state_test_data->lock); ASSERT_TRUE(state_test_data->connection_resumed); aws_mutex_unlock(&state_test_data->lock); - ASSERT_SUCCESS( - aws_mqtt_client_connection_disconnect(state_test_data->mqtt_connection, s_on_disconnect_fn, state_test_data)); - s_wait_for_disconnect_to_complete(state_test_data); + ASSERT_SUCCESS(aws_mqtt_client_connection_disconnect( + state_test_data->mqtt_connection, aws_test311_on_disconnect_fn, state_test_data)); + aws_test311_wait_for_disconnect_to_complete(state_test_data); /* Decode all received packets by mock server */ ASSERT_SUCCESS(mqtt_mock_server_decode_packets(state_test_data->mock_server)); @@ -2205,19 +1566,19 @@ static int s_test_mqtt_connection_disconnect_while_reconnecting(struct aws_alloc .client_id = aws_byte_cursor_from_c_str("client1234"), .host_name = aws_byte_cursor_from_c_str(state_test_data->endpoint.address), .socket_options = &state_test_data->socket_options, - .on_connection_complete = s_on_connection_complete_fn, + .on_connection_complete = aws_test311_on_connection_complete_fn, .ping_timeout_ms = DEFAULT_TEST_PING_TIMEOUT_MS, .keep_alive_time_secs = DEFAULT_TEST_KEEP_ALIVE_S, }; ASSERT_SUCCESS(aws_mqtt_client_connection_connect(state_test_data->mqtt_connection, &connection_options)); - s_wait_for_connection_to_complete(state_test_data); + aws_test311_wait_for_connection_to_complete(state_test_data); mqtt_mock_server_set_max_connack(state_test_data->mock_server, 0); /* shut it down and the client automatically reconnects.*/ aws_channel_shutdown(state_test_data->server_channel, AWS_OP_SUCCESS); - s_wait_for_interrupt_to_complete(state_test_data); + aws_test311_wait_for_interrupt_to_complete(state_test_data); struct aws_byte_cursor pub_topic = aws_byte_cursor_from_c_str("/test/topic"); struct aws_byte_cursor payload_1 = aws_byte_cursor_from_c_str("Test Message 1"); @@ -2234,7 +1595,7 @@ static int s_test_mqtt_connection_disconnect_while_reconnecting(struct aws_alloc AWS_MQTT_QOS_AT_LEAST_ONCE, false, &payload_1, - s_on_op_complete, + aws_test311_on_op_complete, state_test_data) > 0); ASSERT_TRUE( aws_mqtt_client_connection_publish( @@ -2243,15 +1604,15 @@ static int s_test_mqtt_connection_disconnect_while_reconnecting(struct aws_alloc AWS_MQTT_QOS_AT_LEAST_ONCE, false, &payload_2, - s_on_op_complete, + aws_test311_on_op_complete, state_test_data) > 0); - ASSERT_SUCCESS( - aws_mqtt_client_connection_disconnect(state_test_data->mqtt_connection, s_on_disconnect_fn, state_test_data)); - s_wait_for_disconnect_to_complete(state_test_data); + ASSERT_SUCCESS(aws_mqtt_client_connection_disconnect( + state_test_data->mqtt_connection, aws_test311_on_disconnect_fn, state_test_data)); + aws_test311_wait_for_disconnect_to_complete(state_test_data); aws_mqtt_client_connection_release(state_test_data->mqtt_connection); state_test_data->mqtt_connection = NULL; - s_wait_for_ops_completed(state_test_data); + aws_test311_wait_for_ops_completed(state_test_data); return AWS_OP_SUCCESS; } @@ -2281,7 +1642,7 @@ static int s_test_mqtt_connection_closes_while_making_requests_fn(struct aws_all .client_id = aws_byte_cursor_from_c_str("client1234"), .host_name = aws_byte_cursor_from_c_str(state_test_data->endpoint.address), .socket_options = &state_test_data->socket_options, - .on_connection_complete = s_on_connection_complete_fn, + .on_connection_complete = aws_test311_on_connection_complete_fn, .ping_timeout_ms = DEFAULT_TEST_PING_TIMEOUT_MS, .keep_alive_time_secs = DEFAULT_TEST_KEEP_ALIVE_S, }; @@ -2290,7 +1651,7 @@ static int s_test_mqtt_connection_closes_while_making_requests_fn(struct aws_all struct aws_byte_cursor payload_1 = aws_byte_cursor_from_c_str("Test Message 1"); ASSERT_SUCCESS(aws_mqtt_client_connection_connect(state_test_data->mqtt_connection, &connection_options)); - s_wait_for_connection_to_complete(state_test_data); + aws_test311_wait_for_connection_to_complete(state_test_data); aws_mutex_lock(&state_test_data->lock); state_test_data->expected_ops_completed = 1; @@ -2310,16 +1671,16 @@ static int s_test_mqtt_connection_closes_while_making_requests_fn(struct aws_all AWS_MQTT_QOS_AT_LEAST_ONCE, false, &payload_1, - s_on_op_complete, + aws_test311_on_op_complete, state_test_data); ASSERT_TRUE(packet_id_1 > 0); - s_wait_for_reconnect_to_complete(state_test_data); - s_wait_for_ops_completed(state_test_data); + aws_test311_wait_for_reconnect_to_complete(state_test_data); + aws_test311_wait_for_ops_completed(state_test_data); - ASSERT_SUCCESS( - aws_mqtt_client_connection_disconnect(state_test_data->mqtt_connection, s_on_disconnect_fn, state_test_data)); - s_wait_for_disconnect_to_complete(state_test_data); + ASSERT_SUCCESS(aws_mqtt_client_connection_disconnect( + state_test_data->mqtt_connection, aws_test311_on_disconnect_fn, state_test_data)); + aws_test311_wait_for_disconnect_to_complete(state_test_data); return AWS_OP_SUCCESS; } @@ -2381,7 +1742,7 @@ static int s_test_mqtt_connection_resend_packets_fn(struct aws_allocator *alloca .client_id = aws_byte_cursor_from_c_str("client1234"), .host_name = aws_byte_cursor_from_c_str(state_test_data->endpoint.address), .socket_options = &state_test_data->socket_options, - .on_connection_complete = s_on_connection_complete_fn, + .on_connection_complete = aws_test311_on_connection_complete_fn, .ping_timeout_ms = DEFAULT_TEST_PING_TIMEOUT_MS, .keep_alive_time_secs = DEFAULT_TEST_KEEP_ALIVE_S, }; @@ -2393,7 +1754,7 @@ static int s_test_mqtt_connection_resend_packets_fn(struct aws_allocator *alloca struct aws_byte_cursor payload_3 = aws_byte_cursor_from_c_str("Test Message 3"); ASSERT_SUCCESS(aws_mqtt_client_connection_connect(state_test_data->mqtt_connection, &connection_options)); - s_wait_for_connection_to_complete(state_test_data); + aws_test311_wait_for_connection_to_complete(state_test_data); /* Disable the auto ACK packets sent by the server, which blocks the requests to complete */ mqtt_mock_server_disable_auto_ack(state_test_data->mock_server); @@ -2408,10 +1769,10 @@ static int s_test_mqtt_connection_resend_packets_fn(struct aws_allocator *alloca state_test_data->mqtt_connection, &sub_topic, AWS_MQTT_QOS_AT_LEAST_ONCE, - s_on_publish_received, + aws_test311_on_publish_received, state_test_data, NULL, - s_on_suback, + aws_test311_on_suback, state_test_data); ASSERT_TRUE(packet_ids[1] > 0); @@ -2435,15 +1796,15 @@ static int s_test_mqtt_connection_resend_packets_fn(struct aws_allocator *alloca /* shutdown the channel for some error */ aws_channel_shutdown(state_test_data->server_channel, AWS_ERROR_INVALID_STATE); - s_wait_for_reconnect_to_complete(state_test_data); + aws_test311_wait_for_reconnect_to_complete(state_test_data); /* Wait again, and ensure the publishes have been resent */ aws_thread_current_sleep(ONE_SEC); ASSERT_SUCCESS(s_check_resend_packets( state_test_data->mock_server, packet_count, true, packet_ids, AWS_ARRAY_SIZE(packet_ids))); - ASSERT_SUCCESS( - aws_mqtt_client_connection_disconnect(state_test_data->mqtt_connection, s_on_disconnect_fn, state_test_data)); - s_wait_for_disconnect_to_complete(state_test_data); + ASSERT_SUCCESS(aws_mqtt_client_connection_disconnect( + state_test_data->mqtt_connection, aws_test311_on_disconnect_fn, state_test_data)); + aws_test311_wait_for_disconnect_to_complete(state_test_data); return AWS_OP_SUCCESS; } @@ -2468,7 +1829,7 @@ static int s_test_mqtt_connection_not_retry_publish_QoS_0_fn(struct aws_allocato .client_id = aws_byte_cursor_from_c_str("client1234"), .host_name = aws_byte_cursor_from_c_str(state_test_data->endpoint.address), .socket_options = &state_test_data->socket_options, - .on_connection_complete = s_on_connection_complete_fn, + .on_connection_complete = aws_test311_on_connection_complete_fn, .ping_timeout_ms = DEFAULT_TEST_PING_TIMEOUT_MS, .keep_alive_time_secs = 16960, /* basically stop automatically sending PINGREQ */ }; @@ -2477,7 +1838,7 @@ static int s_test_mqtt_connection_not_retry_publish_QoS_0_fn(struct aws_allocato struct aws_byte_cursor payload_1 = aws_byte_cursor_from_c_str("Test Message 1"); ASSERT_SUCCESS(aws_mqtt_client_connection_connect(state_test_data->mqtt_connection, &connection_options)); - s_wait_for_connection_to_complete(state_test_data); + aws_test311_wait_for_connection_to_complete(state_test_data); /* kill the connection */ aws_channel_shutdown(state_test_data->server_channel, AWS_ERROR_INVALID_STATE); @@ -2493,23 +1854,23 @@ static int s_test_mqtt_connection_not_retry_publish_QoS_0_fn(struct aws_allocato AWS_MQTT_QOS_AT_MOST_ONCE, false, &payload_1, - s_on_op_complete, + aws_test311_on_op_complete, state_test_data); ASSERT_TRUE(packet_id_1 > 0); /* publish should complete after the shutdown */ - s_wait_for_ops_completed(state_test_data); + aws_test311_wait_for_ops_completed(state_test_data); /* wait for reconnect */ - s_wait_for_reconnect_to_complete(state_test_data); + aws_test311_wait_for_reconnect_to_complete(state_test_data); /* Check all received packets, no publish packets ever received by the server. Because the connection lost before it * ever get sent. */ ASSERT_SUCCESS(mqtt_mock_server_decode_packets(state_test_data->mock_server)); ASSERT_NULL( mqtt_mock_server_find_decoded_packet_by_type(state_test_data->mock_server, 0, AWS_MQTT_PACKET_PUBLISH, NULL)); - ASSERT_SUCCESS( - aws_mqtt_client_connection_disconnect(state_test_data->mqtt_connection, s_on_disconnect_fn, state_test_data)); - s_wait_for_disconnect_to_complete(state_test_data); + ASSERT_SUCCESS(aws_mqtt_client_connection_disconnect( + state_test_data->mqtt_connection, aws_test311_on_disconnect_fn, state_test_data)); + aws_test311_wait_for_disconnect_to_complete(state_test_data); return AWS_OP_SUCCESS; } @@ -2535,7 +1896,7 @@ static int s_test_mqtt_connection_consistent_retry_policy_fn(struct aws_allocato .client_id = aws_byte_cursor_from_c_str("client1234"), .host_name = aws_byte_cursor_from_c_str(state_test_data->endpoint.address), .socket_options = &state_test_data->socket_options, - .on_connection_complete = s_on_connection_complete_fn, + .on_connection_complete = aws_test311_on_connection_complete_fn, .ping_timeout_ms = DEFAULT_TEST_PING_TIMEOUT_MS, .protocol_operation_timeout_ms = 3000, .keep_alive_time_secs = 16960, /* basically stop automatically sending PINGREQ */ @@ -2546,7 +1907,7 @@ static int s_test_mqtt_connection_consistent_retry_policy_fn(struct aws_allocato struct aws_byte_cursor sub_topic = aws_byte_cursor_from_c_str("/test/topic"); ASSERT_SUCCESS(aws_mqtt_client_connection_connect(state_test_data->mqtt_connection, &connection_options)); - s_wait_for_connection_to_complete(state_test_data); + aws_test311_wait_for_connection_to_complete(state_test_data); struct aws_channel_handler *handler = state_test_data->mock_server; /* Disable the auto ACK packets sent by the server, which blocks the requests to complete */ @@ -2563,7 +1924,7 @@ static int s_test_mqtt_connection_consistent_retry_policy_fn(struct aws_allocato AWS_MQTT_QOS_AT_LEAST_ONCE, false, &payload_1, - s_on_op_complete, + aws_test311_on_op_complete, state_test_data); ASSERT_TRUE(packet_id_1 > 0); /* make another subscribe */ @@ -2574,12 +1935,12 @@ static int s_test_mqtt_connection_consistent_retry_policy_fn(struct aws_allocato NULL, NULL, NULL, - s_on_suback, + aws_test311_on_suback, state_test_data); ASSERT_TRUE(packet_id_2 > 0); /* wait for reconnect */ - s_wait_for_reconnect_to_complete(state_test_data); + aws_test311_wait_for_reconnect_to_complete(state_test_data); /* Wait for 1 sec. ensure all the requests have been received by the server */ aws_thread_current_sleep(ONE_SEC); @@ -2594,9 +1955,9 @@ static int s_test_mqtt_connection_consistent_retry_policy_fn(struct aws_allocato mqtt_mock_server_enable_auto_ack(handler); /* Kill the connection again, the requests will be retried since the response has not been received yet. */ aws_channel_shutdown(state_test_data->server_channel, AWS_ERROR_INVALID_STATE); - s_wait_for_reconnect_to_complete(state_test_data); + aws_test311_wait_for_reconnect_to_complete(state_test_data); /* subscribe should be able to completed now */ - s_wait_for_subscribe_to_complete(state_test_data); + aws_test311_wait_for_subscribe_to_complete(state_test_data); /* Check all received packets, subscribe and publish has been resent */ ASSERT_SUCCESS(mqtt_mock_server_decode_packets(handler)); @@ -2606,9 +1967,9 @@ static int s_test_mqtt_connection_consistent_retry_policy_fn(struct aws_allocato mqtt_mock_server_find_decoded_packet_by_type(handler, packet_count, AWS_MQTT_PACKET_SUBSCRIBE, NULL)); ASSERT_NOT_NULL(mqtt_mock_server_find_decoded_packet_by_type(handler, packet_count, AWS_MQTT_PACKET_PUBLISH, NULL)); - ASSERT_SUCCESS( - aws_mqtt_client_connection_disconnect(state_test_data->mqtt_connection, s_on_disconnect_fn, state_test_data)); - s_wait_for_disconnect_to_complete(state_test_data); + ASSERT_SUCCESS(aws_mqtt_client_connection_disconnect( + state_test_data->mqtt_connection, aws_test311_on_disconnect_fn, state_test_data)); + aws_test311_wait_for_disconnect_to_complete(state_test_data); return AWS_OP_SUCCESS; } @@ -2635,7 +1996,7 @@ static int s_test_mqtt_connection_not_resend_packets_on_healthy_connection_fn( .client_id = aws_byte_cursor_from_c_str("client1234"), .host_name = aws_byte_cursor_from_c_str(state_test_data->endpoint.address), .socket_options = &state_test_data->socket_options, - .on_connection_complete = s_on_connection_complete_fn, + .on_connection_complete = aws_test311_on_connection_complete_fn, .ping_timeout_ms = DEFAULT_TEST_PING_TIMEOUT_MS, .keep_alive_time_secs = DEFAULT_TEST_KEEP_ALIVE_S, }; @@ -2645,7 +2006,7 @@ static int s_test_mqtt_connection_not_resend_packets_on_healthy_connection_fn( struct aws_byte_cursor sub_topic = aws_byte_cursor_from_c_str("/test/topic"); ASSERT_SUCCESS(aws_mqtt_client_connection_connect(state_test_data->mqtt_connection, &connection_options)); - s_wait_for_connection_to_complete(state_test_data); + aws_test311_wait_for_connection_to_complete(state_test_data); struct aws_channel_handler *handler = state_test_data->mock_server; /* Disable the auto ACK packets sent by the server, which blocks the requests to complete */ @@ -2661,7 +2022,7 @@ static int s_test_mqtt_connection_not_resend_packets_on_healthy_connection_fn( AWS_MQTT_QOS_AT_LEAST_ONCE, false, &payload_1, - s_on_op_complete, + aws_test311_on_op_complete, state_test_data); ASSERT_TRUE(packet_id_1 > 0); /* make another subscribe */ @@ -2672,7 +2033,7 @@ static int s_test_mqtt_connection_not_resend_packets_on_healthy_connection_fn( NULL, NULL, NULL, - s_on_suback, + aws_test311_on_suback, state_test_data); ASSERT_TRUE(packet_id_2 > 0); @@ -2693,9 +2054,9 @@ static int s_test_mqtt_connection_not_resend_packets_on_healthy_connection_fn( mqtt_mock_server_find_decoded_packet_by_type(handler, pre_index + 1, AWS_MQTT_PACKET_SUBSCRIBE, NULL)); } - ASSERT_SUCCESS( - aws_mqtt_client_connection_disconnect(state_test_data->mqtt_connection, s_on_disconnect_fn, state_test_data)); - s_wait_for_disconnect_to_complete(state_test_data); + ASSERT_SUCCESS(aws_mqtt_client_connection_disconnect( + state_test_data->mqtt_connection, aws_test311_on_disconnect_fn, state_test_data)); + aws_test311_wait_for_disconnect_to_complete(state_test_data); return AWS_OP_SUCCESS; } @@ -2723,17 +2084,17 @@ static int s_test_mqtt_connection_destory_pending_requests_fn(struct aws_allocat AWS_MQTT_QOS_AT_LEAST_ONCE, false, &payload, - s_on_op_complete, + aws_test311_on_op_complete, state_test_data) > 0); ASSERT_TRUE( aws_mqtt_client_connection_subscribe( state_test_data->mqtt_connection, &topic, AWS_MQTT_QOS_AT_LEAST_ONCE, - s_on_publish_received, + aws_test311_on_publish_received, state_test_data, NULL, - s_on_suback, + aws_test311_on_suback, state_test_data) > 0); return AWS_OP_SUCCESS; @@ -2757,13 +2118,13 @@ static int s_test_mqtt_clean_session_not_retry_fn(struct aws_allocator *allocato .client_id = aws_byte_cursor_from_c_str("client1234"), .host_name = aws_byte_cursor_from_c_str(state_test_data->endpoint.address), .socket_options = &state_test_data->socket_options, - .on_connection_complete = s_on_connection_complete_fn, + .on_connection_complete = aws_test311_on_connection_complete_fn, .ping_timeout_ms = DEFAULT_TEST_PING_TIMEOUT_MS, .keep_alive_time_secs = DEFAULT_TEST_KEEP_ALIVE_S, }; ASSERT_SUCCESS(aws_mqtt_client_connection_connect(state_test_data->mqtt_connection, &connection_options)); - s_wait_for_connection_to_complete(state_test_data); + aws_test311_wait_for_connection_to_complete(state_test_data); struct aws_channel_handler *handler = state_test_data->mock_server; mqtt_mock_server_disable_auto_ack(handler); @@ -2777,33 +2138,33 @@ static int s_test_mqtt_clean_session_not_retry_fn(struct aws_allocator *allocato AWS_MQTT_QOS_AT_LEAST_ONCE, false, &payload, - s_on_op_complete, + aws_test311_on_op_complete, state_test_data) > 0); ASSERT_TRUE( aws_mqtt_client_connection_subscribe( state_test_data->mqtt_connection, &topic, AWS_MQTT_QOS_AT_LEAST_ONCE, - s_on_publish_received, + aws_test311_on_publish_received, state_test_data, NULL, - s_on_suback, + aws_test311_on_suback, state_test_data) > 0); aws_mutex_lock(&state_test_data->lock); state_test_data->expected_ops_completed = 1; aws_mutex_unlock(&state_test_data->lock); /* Shutdown the connection */ aws_channel_shutdown(state_test_data->server_channel, AWS_OP_SUCCESS); - s_wait_for_interrupt_to_complete(state_test_data); + aws_test311_wait_for_interrupt_to_complete(state_test_data); /* Once the connection lost, the requests will fail */ ASSERT_UINT_EQUALS(state_test_data->op_complete_error, AWS_ERROR_MQTT_CANCELLED_FOR_CLEAN_SESSION); ASSERT_UINT_EQUALS(state_test_data->subscribe_complete_error, AWS_ERROR_MQTT_CANCELLED_FOR_CLEAN_SESSION); /* Disconnect */ - ASSERT_SUCCESS( - aws_mqtt_client_connection_disconnect(state_test_data->mqtt_connection, s_on_disconnect_fn, state_test_data)); - s_wait_for_disconnect_to_complete(state_test_data); + ASSERT_SUCCESS(aws_mqtt_client_connection_disconnect( + state_test_data->mqtt_connection, aws_test311_on_disconnect_fn, state_test_data)); + aws_test311_wait_for_disconnect_to_complete(state_test_data); return AWS_OP_SUCCESS; } @@ -2826,7 +2187,7 @@ static int s_test_mqtt_clean_session_discard_previous_fn(struct aws_allocator *a .client_id = aws_byte_cursor_from_c_str("client1234"), .host_name = aws_byte_cursor_from_c_str(state_test_data->endpoint.address), .socket_options = &state_test_data->socket_options, - .on_connection_complete = s_on_connection_complete_fn, + .on_connection_complete = aws_test311_on_connection_complete_fn, .ping_timeout_ms = DEFAULT_TEST_PING_TIMEOUT_MS, .keep_alive_time_secs = 16960, /* basically stop automatically sending PINGREQ */ }; @@ -2844,25 +2205,25 @@ static int s_test_mqtt_clean_session_discard_previous_fn(struct aws_allocator *a AWS_MQTT_QOS_AT_LEAST_ONCE, false, &payload, - s_on_op_complete, + aws_test311_on_op_complete, state_test_data) > 0); ASSERT_TRUE( aws_mqtt_client_connection_subscribe( state_test_data->mqtt_connection, &topic, AWS_MQTT_QOS_AT_LEAST_ONCE, - s_on_publish_received, + aws_test311_on_publish_received, state_test_data, NULL, - s_on_suback, + aws_test311_on_suback, state_test_data) > 0); ASSERT_SUCCESS(aws_mqtt_client_connection_connect(state_test_data->mqtt_connection, &connection_options)); - s_wait_for_connection_to_complete(state_test_data); + aws_test311_wait_for_connection_to_complete(state_test_data); struct aws_channel_handler *handler = state_test_data->mock_server; - s_wait_for_ops_completed(state_test_data); - s_wait_for_subscribe_to_complete(state_test_data); + aws_test311_wait_for_ops_completed(state_test_data); + aws_test311_wait_for_subscribe_to_complete(state_test_data); ASSERT_UINT_EQUALS(state_test_data->op_complete_error, AWS_ERROR_MQTT_CANCELLED_FOR_CLEAN_SESSION); ASSERT_UINT_EQUALS(state_test_data->subscribe_complete_error, AWS_ERROR_MQTT_CANCELLED_FOR_CLEAN_SESSION); @@ -2872,9 +2233,9 @@ static int s_test_mqtt_clean_session_discard_previous_fn(struct aws_allocator *a ASSERT_NULL(mqtt_mock_server_find_decoded_packet_by_type(handler, 0, AWS_MQTT_PACKET_SUBSCRIBE, NULL)); /* Disconnect */ - ASSERT_SUCCESS( - aws_mqtt_client_connection_disconnect(state_test_data->mqtt_connection, s_on_disconnect_fn, state_test_data)); - s_wait_for_disconnect_to_complete(state_test_data); + ASSERT_SUCCESS(aws_mqtt_client_connection_disconnect( + state_test_data->mqtt_connection, aws_test311_on_disconnect_fn, state_test_data)); + aws_test311_wait_for_disconnect_to_complete(state_test_data); return AWS_OP_SUCCESS; } @@ -2896,7 +2257,7 @@ static int s_test_mqtt_clean_session_keep_next_session_fn(struct aws_allocator * .client_id = aws_byte_cursor_from_c_str("client1234"), .host_name = aws_byte_cursor_from_c_str(state_test_data->endpoint.address), .socket_options = &state_test_data->socket_options, - .on_connection_complete = s_on_connection_complete_fn, + .on_connection_complete = aws_test311_on_connection_complete_fn, .ping_timeout_ms = DEFAULT_TEST_PING_TIMEOUT_MS, .keep_alive_time_secs = 16960, /* basically stop automatically sending PINGREQ */ }; @@ -2916,23 +2277,23 @@ static int s_test_mqtt_clean_session_keep_next_session_fn(struct aws_allocator * AWS_MQTT_QOS_AT_LEAST_ONCE, false, &payload, - s_on_op_complete, + aws_test311_on_op_complete, state_test_data) > 0); ASSERT_TRUE( aws_mqtt_client_connection_subscribe( state_test_data->mqtt_connection, &topic, AWS_MQTT_QOS_AT_LEAST_ONCE, - s_on_publish_received, + aws_test311_on_publish_received, state_test_data, NULL, - s_on_suback, + aws_test311_on_suback, state_test_data) > 0); - s_wait_for_connection_to_complete(state_test_data); + aws_test311_wait_for_connection_to_complete(state_test_data); struct aws_channel_handler *handler = state_test_data->mock_server; - s_wait_for_ops_completed(state_test_data); - s_wait_for_subscribe_to_complete(state_test_data); + aws_test311_wait_for_ops_completed(state_test_data); + aws_test311_wait_for_subscribe_to_complete(state_test_data); ASSERT_UINT_EQUALS(state_test_data->op_complete_error, AWS_ERROR_SUCCESS); ASSERT_UINT_EQUALS(state_test_data->subscribe_complete_error, AWS_ERROR_SUCCESS); @@ -2942,9 +2303,9 @@ static int s_test_mqtt_clean_session_keep_next_session_fn(struct aws_allocator * ASSERT_NOT_NULL(mqtt_mock_server_find_decoded_packet_by_type(handler, 0, AWS_MQTT_PACKET_SUBSCRIBE, NULL)); /* Disconnect */ - ASSERT_SUCCESS( - aws_mqtt_client_connection_disconnect(state_test_data->mqtt_connection, s_on_disconnect_fn, state_test_data)); - s_wait_for_disconnect_to_complete(state_test_data); + ASSERT_SUCCESS(aws_mqtt_client_connection_disconnect( + state_test_data->mqtt_connection, aws_test311_on_disconnect_fn, state_test_data)); + aws_test311_wait_for_disconnect_to_complete(state_test_data); return AWS_OP_SUCCESS; } @@ -2968,7 +2329,7 @@ static int s_test_mqtt_connection_publish_QoS1_timeout_fn(struct aws_allocator * .client_id = aws_byte_cursor_from_c_str("client1234"), .host_name = aws_byte_cursor_from_c_str(state_test_data->endpoint.address), .socket_options = &state_test_data->socket_options, - .on_connection_complete = s_on_connection_complete_fn, + .on_connection_complete = aws_test311_on_connection_complete_fn, .ping_timeout_ms = DEFAULT_TEST_PING_TIMEOUT_MS, .protocol_operation_timeout_ms = 3000, .keep_alive_time_secs = 16960, /* basically stop automatically sending PINGREQ */ @@ -2978,7 +2339,7 @@ static int s_test_mqtt_connection_publish_QoS1_timeout_fn(struct aws_allocator * struct aws_byte_cursor payload_1 = aws_byte_cursor_from_c_str("Test Message 1"); ASSERT_SUCCESS(aws_mqtt_client_connection_connect(state_test_data->mqtt_connection, &connection_options)); - s_wait_for_connection_to_complete(state_test_data); + aws_test311_wait_for_connection_to_complete(state_test_data); /* Disable the auto ACK packets sent by the server, which blocks the requests to complete */ mqtt_mock_server_disable_auto_ack(state_test_data->mock_server); @@ -2993,17 +2354,17 @@ static int s_test_mqtt_connection_publish_QoS1_timeout_fn(struct aws_allocator * AWS_MQTT_QOS_AT_LEAST_ONCE, false, &payload_1, - s_on_op_complete, + aws_test311_on_op_complete, state_test_data); ASSERT_TRUE(packet_id_1 > 0); /* publish should complete after the shutdown */ - s_wait_for_ops_completed(state_test_data); + aws_test311_wait_for_ops_completed(state_test_data); /* Check the publish has been completed with timeout error */ ASSERT_UINT_EQUALS(state_test_data->op_complete_error, AWS_ERROR_MQTT_TIMEOUT); - ASSERT_SUCCESS( - aws_mqtt_client_connection_disconnect(state_test_data->mqtt_connection, s_on_disconnect_fn, state_test_data)); - s_wait_for_disconnect_to_complete(state_test_data); + ASSERT_SUCCESS(aws_mqtt_client_connection_disconnect( + state_test_data->mqtt_connection, aws_test311_on_disconnect_fn, state_test_data)); + aws_test311_wait_for_disconnect_to_complete(state_test_data); return AWS_OP_SUCCESS; } @@ -3028,7 +2389,7 @@ static int s_test_mqtt_connection_unsub_timeout_fn(struct aws_allocator *allocat .client_id = aws_byte_cursor_from_c_str("client1234"), .host_name = aws_byte_cursor_from_c_str(state_test_data->endpoint.address), .socket_options = &state_test_data->socket_options, - .on_connection_complete = s_on_connection_complete_fn, + .on_connection_complete = aws_test311_on_connection_complete_fn, .ping_timeout_ms = DEFAULT_TEST_PING_TIMEOUT_MS, .protocol_operation_timeout_ms = 3000, .keep_alive_time_secs = 16960, /* basically stop automatically sending PINGREQ */ @@ -3037,7 +2398,7 @@ static int s_test_mqtt_connection_unsub_timeout_fn(struct aws_allocator *allocat struct aws_byte_cursor pub_topic = aws_byte_cursor_from_c_str("/test/topic"); ASSERT_SUCCESS(aws_mqtt_client_connection_connect(state_test_data->mqtt_connection, &connection_options)); - s_wait_for_connection_to_complete(state_test_data); + aws_test311_wait_for_connection_to_complete(state_test_data); /* Disable the auto ACK packets sent by the server, which blocks the requests to complete */ mqtt_mock_server_disable_auto_ack(state_test_data->mock_server); @@ -3048,16 +2409,16 @@ static int s_test_mqtt_connection_unsub_timeout_fn(struct aws_allocator *allocat /* unsubscribe to the first topic */ uint16_t unsub_packet_id = aws_mqtt_client_connection_unsubscribe( - state_test_data->mqtt_connection, &pub_topic, s_on_op_complete, state_test_data); + state_test_data->mqtt_connection, &pub_topic, aws_test311_on_op_complete, state_test_data); ASSERT_TRUE(unsub_packet_id > 0); /* publish should complete after the shutdown */ - s_wait_for_ops_completed(state_test_data); + aws_test311_wait_for_ops_completed(state_test_data); /* Check the publish has been completed with timeout error */ ASSERT_UINT_EQUALS(state_test_data->op_complete_error, AWS_ERROR_MQTT_TIMEOUT); - ASSERT_SUCCESS( - aws_mqtt_client_connection_disconnect(state_test_data->mqtt_connection, s_on_disconnect_fn, state_test_data)); - s_wait_for_disconnect_to_complete(state_test_data); + ASSERT_SUCCESS(aws_mqtt_client_connection_disconnect( + state_test_data->mqtt_connection, aws_test311_on_disconnect_fn, state_test_data)); + aws_test311_wait_for_disconnect_to_complete(state_test_data); return AWS_OP_SUCCESS; } @@ -3084,7 +2445,7 @@ static int s_test_mqtt_connection_publish_QoS1_timeout_connection_lost_reset_tim .client_id = aws_byte_cursor_from_c_str("client1234"), .host_name = aws_byte_cursor_from_c_str(state_test_data->endpoint.address), .socket_options = &state_test_data->socket_options, - .on_connection_complete = s_on_connection_complete_fn, + .on_connection_complete = aws_test311_on_connection_complete_fn, .ping_timeout_ms = DEFAULT_TEST_PING_TIMEOUT_MS, .protocol_operation_timeout_ms = 3000, .keep_alive_time_secs = 16960, /* basically stop automatically sending PINGREQ */ @@ -3094,7 +2455,7 @@ static int s_test_mqtt_connection_publish_QoS1_timeout_connection_lost_reset_tim struct aws_byte_cursor payload_1 = aws_byte_cursor_from_c_str("Test Message 1"); ASSERT_SUCCESS(aws_mqtt_client_connection_connect(state_test_data->mqtt_connection, &connection_options)); - s_wait_for_connection_to_complete(state_test_data); + aws_test311_wait_for_connection_to_complete(state_test_data); /* Disable the auto ACK packets sent by the server, which blocks the requests to complete */ mqtt_mock_server_disable_auto_ack(state_test_data->mock_server); @@ -3109,7 +2470,7 @@ static int s_test_mqtt_connection_publish_QoS1_timeout_connection_lost_reset_tim AWS_MQTT_QOS_AT_LEAST_ONCE, false, &payload_1, - s_on_op_complete, + aws_test311_on_op_complete, state_test_data); ASSERT_TRUE(packet_id_1 > 0); @@ -3117,7 +2478,7 @@ static int s_test_mqtt_connection_publish_QoS1_timeout_connection_lost_reset_tim aws_thread_current_sleep((uint64_t)ONE_SEC * 2); /* Kill the connection, the requests will be retried and the timeout will be reset. */ aws_channel_shutdown(state_test_data->server_channel, AWS_ERROR_INVALID_STATE); - s_wait_for_reconnect_to_complete(state_test_data); + aws_test311_wait_for_reconnect_to_complete(state_test_data); /* sleep for 2 sec again, in total the response has not received for more than 4 sec, timeout should happen if the * lost of connection not reset the timeout */ aws_thread_current_sleep((uint64_t)ONE_SEC * 2); @@ -3125,12 +2486,12 @@ static int s_test_mqtt_connection_publish_QoS1_timeout_connection_lost_reset_tim ASSERT_SUCCESS(mqtt_mock_server_send_puback(state_test_data->mock_server, packet_id_1)); /* publish should complete after the shutdown */ - s_wait_for_ops_completed(state_test_data); + aws_test311_wait_for_ops_completed(state_test_data); /* Check the publish has been completed successfully since the lost of the connection reset the timeout */ ASSERT_UINT_EQUALS(state_test_data->op_complete_error, AWS_ERROR_SUCCESS); - ASSERT_SUCCESS( - aws_mqtt_client_connection_disconnect(state_test_data->mqtt_connection, s_on_disconnect_fn, state_test_data)); - s_wait_for_disconnect_to_complete(state_test_data); + ASSERT_SUCCESS(aws_mqtt_client_connection_disconnect( + state_test_data->mqtt_connection, aws_test311_on_disconnect_fn, state_test_data)); + aws_test311_wait_for_disconnect_to_complete(state_test_data); return AWS_OP_SUCCESS; } @@ -3170,21 +2531,21 @@ static int s_test_mqtt_connection_close_callback_simple_fn(struct aws_allocator .client_id = aws_byte_cursor_from_c_str("client1234"), .host_name = aws_byte_cursor_from_c_str(state_test_data->endpoint.address), .socket_options = &state_test_data->socket_options, - .on_connection_complete = s_on_connection_complete_fn, + .on_connection_complete = aws_test311_on_connection_complete_fn, }; aws_mqtt_client_connection_set_connection_closed_handler( state_test_data->mqtt_connection, s_on_connection_closed_fn, state_test_data); ASSERT_SUCCESS(aws_mqtt_client_connection_connect(state_test_data->mqtt_connection, &connection_options)); - s_wait_for_connection_to_complete(state_test_data); + aws_test311_wait_for_connection_to_complete(state_test_data); /* sleep for 2 sec, just to make sure the connection is stable */ aws_thread_current_sleep((uint64_t)ONE_SEC * 2); /* Disconnect */ - ASSERT_SUCCESS( - aws_mqtt_client_connection_disconnect(state_test_data->mqtt_connection, s_on_disconnect_fn, state_test_data)); - s_wait_for_disconnect_to_complete(state_test_data); + ASSERT_SUCCESS(aws_mqtt_client_connection_disconnect( + state_test_data->mqtt_connection, aws_test311_on_disconnect_fn, state_test_data)); + aws_test311_wait_for_disconnect_to_complete(state_test_data); /* Make sure the callback was called and the value is what we expect */ ASSERT_UINT_EQUALS(1, state_test_data->connection_close_calls); @@ -3212,25 +2573,25 @@ static int s_test_mqtt_connection_close_callback_interrupted_fn(struct aws_alloc .client_id = aws_byte_cursor_from_c_str("client1234"), .host_name = aws_byte_cursor_from_c_str(state_test_data->endpoint.address), .socket_options = &state_test_data->socket_options, - .on_connection_complete = s_on_connection_complete_fn, + .on_connection_complete = aws_test311_on_connection_complete_fn, }; aws_mqtt_client_connection_set_connection_closed_handler( state_test_data->mqtt_connection, s_on_connection_closed_fn, state_test_data); ASSERT_SUCCESS(aws_mqtt_client_connection_connect(state_test_data->mqtt_connection, &connection_options)); - s_wait_for_connection_to_complete(state_test_data); + aws_test311_wait_for_connection_to_complete(state_test_data); /* Kill the connection */ aws_channel_shutdown(state_test_data->server_channel, AWS_ERROR_INVALID_STATE); - s_wait_for_reconnect_to_complete(state_test_data); + aws_test311_wait_for_reconnect_to_complete(state_test_data); /* sleep for 2 sec, just to make sure the connection is stable */ aws_thread_current_sleep((uint64_t)ONE_SEC * 2); /* Disconnect */ - ASSERT_SUCCESS( - aws_mqtt_client_connection_disconnect(state_test_data->mqtt_connection, s_on_disconnect_fn, state_test_data)); - s_wait_for_disconnect_to_complete(state_test_data); + ASSERT_SUCCESS(aws_mqtt_client_connection_disconnect( + state_test_data->mqtt_connection, aws_test311_on_disconnect_fn, state_test_data)); + aws_test311_wait_for_disconnect_to_complete(state_test_data); /* Make sure the callback was called only ONCE and the value is what we expect */ ASSERT_UINT_EQUALS(1, state_test_data->connection_close_calls); @@ -3258,7 +2619,7 @@ static int s_test_mqtt_connection_close_callback_multi_fn(struct aws_allocator * .client_id = aws_byte_cursor_from_c_str("client1234"), .host_name = aws_byte_cursor_from_c_str(state_test_data->endpoint.address), .socket_options = &state_test_data->socket_options, - .on_connection_complete = s_on_connection_complete_fn, + .on_connection_complete = aws_test311_on_connection_complete_fn, }; aws_mqtt_client_connection_set_connection_closed_handler( state_test_data->mqtt_connection, s_on_connection_closed_fn, state_test_data); @@ -3266,12 +2627,12 @@ static int s_test_mqtt_connection_close_callback_multi_fn(struct aws_allocator * int disconnect_amount = 10; for (int i = 0; i < disconnect_amount; i++) { ASSERT_SUCCESS(aws_mqtt_client_connection_connect(state_test_data->mqtt_connection, &connection_options)); - s_wait_for_connection_to_complete(state_test_data); + aws_test311_wait_for_connection_to_complete(state_test_data); /* Disconnect */ ASSERT_SUCCESS(aws_mqtt_client_connection_disconnect( - state_test_data->mqtt_connection, s_on_disconnect_fn, state_test_data)); - s_wait_for_disconnect_to_complete(state_test_data); + state_test_data->mqtt_connection, aws_test311_on_disconnect_fn, state_test_data)); + aws_test311_wait_for_disconnect_to_complete(state_test_data); } /* Make sure the callback was called disconnect_amount times */ @@ -3299,11 +2660,11 @@ static int s_test_mqtt_connection_reconnection_backoff_stable(struct aws_allocat .client_id = aws_byte_cursor_from_c_str("client1234"), .host_name = aws_byte_cursor_from_c_str(state_test_data->endpoint.address), .socket_options = &state_test_data->socket_options, - .on_connection_complete = s_on_connection_complete_fn, + .on_connection_complete = aws_test311_on_connection_complete_fn, }; ASSERT_SUCCESS(aws_mqtt_client_connection_connect(state_test_data->mqtt_connection, &connection_options)); - s_wait_for_connection_to_complete(state_test_data); + aws_test311_wait_for_connection_to_complete(state_test_data); uint64_t time_before = 0; uint64_t time_after = 0; @@ -3317,7 +2678,7 @@ static int s_test_mqtt_connection_reconnection_backoff_stable(struct aws_allocat /* shut it down and make sure the client automatically reconnects.*/ aws_channel_shutdown(state_test_data->server_channel, AWS_OP_SUCCESS); - s_wait_for_reconnect_to_complete(state_test_data); + aws_test311_wait_for_reconnect_to_complete(state_test_data); aws_high_res_clock_get_ticks(&time_after); @@ -3330,9 +2691,9 @@ static int s_test_mqtt_connection_reconnection_backoff_stable(struct aws_allocat } /* Disconnect */ - ASSERT_SUCCESS( - aws_mqtt_client_connection_disconnect(state_test_data->mqtt_connection, s_on_disconnect_fn, state_test_data)); - s_wait_for_disconnect_to_complete(state_test_data); + ASSERT_SUCCESS(aws_mqtt_client_connection_disconnect( + state_test_data->mqtt_connection, aws_test311_on_disconnect_fn, state_test_data)); + aws_test311_wait_for_disconnect_to_complete(state_test_data); return AWS_OP_SUCCESS; } @@ -3356,11 +2717,11 @@ static int s_test_mqtt_connection_reconnection_backoff_unstable(struct aws_alloc .client_id = aws_byte_cursor_from_c_str("client1234"), .host_name = aws_byte_cursor_from_c_str(state_test_data->endpoint.address), .socket_options = &state_test_data->socket_options, - .on_connection_complete = s_on_connection_complete_fn, + .on_connection_complete = aws_test311_on_connection_complete_fn, }; ASSERT_SUCCESS(aws_mqtt_client_connection_connect(state_test_data->mqtt_connection, &connection_options)); - s_wait_for_connection_to_complete(state_test_data); + aws_test311_wait_for_connection_to_complete(state_test_data); uint64_t time_before = 0; uint64_t time_after = 0; @@ -3371,7 +2732,7 @@ static int s_test_mqtt_connection_reconnection_backoff_unstable(struct aws_alloc /* shut it down and make sure the client automatically reconnects.*/ aws_channel_shutdown(state_test_data->server_channel, AWS_OP_SUCCESS); - s_wait_for_reconnect_to_complete(state_test_data); + aws_test311_wait_for_reconnect_to_complete(state_test_data); aws_high_res_clock_get_ticks(&time_after); @@ -3387,9 +2748,9 @@ static int s_test_mqtt_connection_reconnection_backoff_unstable(struct aws_alloc } /* Disconnect */ - ASSERT_SUCCESS( - aws_mqtt_client_connection_disconnect(state_test_data->mqtt_connection, s_on_disconnect_fn, state_test_data)); - s_wait_for_disconnect_to_complete(state_test_data); + ASSERT_SUCCESS(aws_mqtt_client_connection_disconnect( + state_test_data->mqtt_connection, aws_test311_on_disconnect_fn, state_test_data)); + aws_test311_wait_for_disconnect_to_complete(state_test_data); return AWS_OP_SUCCESS; } @@ -3413,11 +2774,11 @@ static int s_test_mqtt_connection_reconnection_backoff_reset(struct aws_allocato .client_id = aws_byte_cursor_from_c_str("client1234"), .host_name = aws_byte_cursor_from_c_str(state_test_data->endpoint.address), .socket_options = &state_test_data->socket_options, - .on_connection_complete = s_on_connection_complete_fn, + .on_connection_complete = aws_test311_on_connection_complete_fn, }; ASSERT_SUCCESS(aws_mqtt_client_connection_connect(state_test_data->mqtt_connection, &connection_options)); - s_wait_for_connection_to_complete(state_test_data); + aws_test311_wait_for_connection_to_complete(state_test_data); uint64_t time_before = 0; uint64_t time_after = 0; @@ -3429,7 +2790,7 @@ static int s_test_mqtt_connection_reconnection_backoff_reset(struct aws_allocato /* shut it down and make sure the client automatically reconnects.*/ aws_channel_shutdown(state_test_data->server_channel, AWS_OP_SUCCESS); - s_wait_for_reconnect_to_complete(state_test_data); + aws_test311_wait_for_reconnect_to_complete(state_test_data); aws_high_res_clock_get_ticks(&time_after); reconnection_backoff = time_after - time_before; @@ -3449,7 +2810,7 @@ static int s_test_mqtt_connection_reconnection_backoff_reset(struct aws_allocato /* shut it down and make sure the client automatically reconnects.*/ aws_channel_shutdown(state_test_data->server_channel, AWS_OP_SUCCESS); - s_wait_for_reconnect_to_complete(state_test_data); + aws_test311_wait_for_reconnect_to_complete(state_test_data); aws_high_res_clock_get_ticks(&time_after); reconnection_backoff = time_after - time_before; @@ -3460,9 +2821,9 @@ static int s_test_mqtt_connection_reconnection_backoff_reset(struct aws_allocato ASSERT_TRUE(remainder <= RECONNECT_BACKOFF_DELAY_ERROR_MARGIN_NANO_SECONDS); /* Disconnect */ - ASSERT_SUCCESS( - aws_mqtt_client_connection_disconnect(state_test_data->mqtt_connection, s_on_disconnect_fn, state_test_data)); - s_wait_for_disconnect_to_complete(state_test_data); + ASSERT_SUCCESS(aws_mqtt_client_connection_disconnect( + state_test_data->mqtt_connection, aws_test311_on_disconnect_fn, state_test_data)); + aws_test311_wait_for_disconnect_to_complete(state_test_data); return AWS_OP_SUCCESS; } @@ -3488,11 +2849,11 @@ static int s_test_mqtt_connection_reconnection_backoff_reset_after_disconnection .client_id = aws_byte_cursor_from_c_str("client1234"), .host_name = aws_byte_cursor_from_c_str(state_test_data->endpoint.address), .socket_options = &state_test_data->socket_options, - .on_connection_complete = s_on_connection_complete_fn, + .on_connection_complete = aws_test311_on_connection_complete_fn, }; ASSERT_SUCCESS(aws_mqtt_client_connection_connect(state_test_data->mqtt_connection, &connection_options)); - s_wait_for_connection_to_complete(state_test_data); + aws_test311_wait_for_connection_to_complete(state_test_data); uint64_t time_before = 0; uint64_t time_after = 0; @@ -3503,7 +2864,7 @@ static int s_test_mqtt_connection_reconnection_backoff_reset_after_disconnection /* shut it down and make sure the client automatically reconnects.*/ aws_channel_shutdown(state_test_data->server_channel, AWS_OP_SUCCESS); - s_wait_for_reconnect_to_complete(state_test_data); + aws_test311_wait_for_reconnect_to_complete(state_test_data); aws_high_res_clock_get_ticks(&time_after); reconnection_backoff = time_after - time_before; @@ -3514,18 +2875,18 @@ static int s_test_mqtt_connection_reconnection_backoff_reset_after_disconnection expected_reconnect_backoff = aws_min_u64(expected_reconnect_backoff * 2, 10); } /* Disconnect */ - ASSERT_SUCCESS( - aws_mqtt_client_connection_disconnect(state_test_data->mqtt_connection, s_on_disconnect_fn, state_test_data)); - s_wait_for_disconnect_to_complete(state_test_data); + ASSERT_SUCCESS(aws_mqtt_client_connection_disconnect( + state_test_data->mqtt_connection, aws_test311_on_disconnect_fn, state_test_data)); + aws_test311_wait_for_disconnect_to_complete(state_test_data); /* connect again */ ASSERT_SUCCESS(aws_mqtt_client_connection_connect(state_test_data->mqtt_connection, &connection_options)); - s_wait_for_connection_to_complete(state_test_data); + aws_test311_wait_for_connection_to_complete(state_test_data); aws_high_res_clock_get_ticks(&time_before); aws_channel_shutdown(state_test_data->server_channel, AWS_OP_SUCCESS); - s_wait_for_reconnect_to_complete(state_test_data); + aws_test311_wait_for_reconnect_to_complete(state_test_data); aws_high_res_clock_get_ticks(&time_after); reconnection_backoff = time_after - time_before; @@ -3536,9 +2897,9 @@ static int s_test_mqtt_connection_reconnection_backoff_reset_after_disconnection ASSERT_TRUE(remainder <= RECONNECT_BACKOFF_DELAY_ERROR_MARGIN_NANO_SECONDS); /* Disconnect */ - ASSERT_SUCCESS( - aws_mqtt_client_connection_disconnect(state_test_data->mqtt_connection, s_on_disconnect_fn, state_test_data)); - s_wait_for_disconnect_to_complete(state_test_data); + ASSERT_SUCCESS(aws_mqtt_client_connection_disconnect( + state_test_data->mqtt_connection, aws_test311_on_disconnect_fn, state_test_data)); + aws_test311_wait_for_disconnect_to_complete(state_test_data); return AWS_OP_SUCCESS; } @@ -3563,7 +2924,7 @@ static int s_test_mqtt_connection_ping_norm_fn(struct aws_allocator *allocator, .client_id = aws_byte_cursor_from_c_str("client1234"), .host_name = aws_byte_cursor_from_c_str(state_test_data->endpoint.address), .socket_options = &state_test_data->socket_options, - .on_connection_complete = s_on_connection_complete_fn, + .on_connection_complete = aws_test311_on_connection_complete_fn, .keep_alive_time_secs = 1, .ping_timeout_ms = 100, }; @@ -3576,9 +2937,9 @@ static int s_test_mqtt_connection_ping_norm_fn(struct aws_allocator *allocator, /* Ensure the server got 4 PING packets */ ASSERT_INT_EQUALS(4, mqtt_mock_server_get_ping_count(state_test_data->mock_server)); - ASSERT_SUCCESS( - aws_mqtt_client_connection_disconnect(state_test_data->mqtt_connection, s_on_disconnect_fn, state_test_data)); - s_wait_for_disconnect_to_complete(state_test_data); + ASSERT_SUCCESS(aws_mqtt_client_connection_disconnect( + state_test_data->mqtt_connection, aws_test311_on_disconnect_fn, state_test_data)); + aws_test311_wait_for_disconnect_to_complete(state_test_data); return AWS_OP_SUCCESS; } @@ -3604,13 +2965,13 @@ static int s_test_mqtt_connection_ping_no_fn(struct aws_allocator *allocator, vo .client_id = aws_byte_cursor_from_c_str("client1234"), .host_name = aws_byte_cursor_from_c_str(state_test_data->endpoint.address), .socket_options = &state_test_data->socket_options, - .on_connection_complete = s_on_connection_complete_fn, + .on_connection_complete = aws_test311_on_connection_complete_fn, .keep_alive_time_secs = 1, .ping_timeout_ms = 100, }; ASSERT_SUCCESS(aws_mqtt_client_connection_connect(state_test_data->mqtt_connection, &connection_options)); - s_wait_for_connection_to_complete(state_test_data); + aws_test311_wait_for_connection_to_complete(state_test_data); struct aws_byte_cursor pub_topic = aws_byte_cursor_from_c_str("/test/topic"); struct aws_byte_cursor payload_1 = aws_byte_cursor_from_c_str("Test Message 1"); @@ -3630,7 +2991,7 @@ static int s_test_mqtt_connection_ping_no_fn(struct aws_allocator *allocator, vo AWS_MQTT_QOS_AT_LEAST_ONCE, false, &payload_1, - s_on_op_complete, + aws_test311_on_op_complete, state_test_data); ASSERT_TRUE(packet_id > 0); @@ -3645,9 +3006,9 @@ static int s_test_mqtt_connection_ping_no_fn(struct aws_allocator *allocator, vo /* Ensure the server got 0 PING packets */ ASSERT_INT_EQUALS(0, mqtt_mock_server_get_ping_count(state_test_data->mock_server)); - ASSERT_SUCCESS( - aws_mqtt_client_connection_disconnect(state_test_data->mqtt_connection, s_on_disconnect_fn, state_test_data)); - s_wait_for_disconnect_to_complete(state_test_data); + ASSERT_SUCCESS(aws_mqtt_client_connection_disconnect( + state_test_data->mqtt_connection, aws_test311_on_disconnect_fn, state_test_data)); + aws_test311_wait_for_disconnect_to_complete(state_test_data); return AWS_OP_SUCCESS; } @@ -3673,13 +3034,13 @@ static int s_test_mqtt_connection_ping_noack_fn(struct aws_allocator *allocator, .client_id = aws_byte_cursor_from_c_str("client1234"), .host_name = aws_byte_cursor_from_c_str(state_test_data->endpoint.address), .socket_options = &state_test_data->socket_options, - .on_connection_complete = s_on_connection_complete_fn, + .on_connection_complete = aws_test311_on_connection_complete_fn, .keep_alive_time_secs = 1, .ping_timeout_ms = 100, }; ASSERT_SUCCESS(aws_mqtt_client_connection_connect(state_test_data->mqtt_connection, &connection_options)); - s_wait_for_connection_to_complete(state_test_data); + aws_test311_wait_for_connection_to_complete(state_test_data); struct aws_byte_cursor pub_topic = aws_byte_cursor_from_c_str("/test/topic"); struct aws_byte_cursor payload_1 = aws_byte_cursor_from_c_str("Test Message 1"); @@ -3699,7 +3060,7 @@ static int s_test_mqtt_connection_ping_noack_fn(struct aws_allocator *allocator, AWS_MQTT_QOS_AT_MOST_ONCE, false, &payload_1, - s_on_op_complete, + aws_test311_on_op_complete, state_test_data); ASSERT_TRUE(packet_id > 0); @@ -3713,9 +3074,9 @@ static int s_test_mqtt_connection_ping_noack_fn(struct aws_allocator *allocator, /* Ensure the server got 4 PING packets */ ASSERT_INT_EQUALS(4, mqtt_mock_server_get_ping_count(state_test_data->mock_server)); - ASSERT_SUCCESS( - aws_mqtt_client_connection_disconnect(state_test_data->mqtt_connection, s_on_disconnect_fn, state_test_data)); - s_wait_for_disconnect_to_complete(state_test_data); + ASSERT_SUCCESS(aws_mqtt_client_connection_disconnect( + state_test_data->mqtt_connection, aws_test311_on_disconnect_fn, state_test_data)); + aws_test311_wait_for_disconnect_to_complete(state_test_data); return AWS_OP_SUCCESS; } @@ -3747,13 +3108,13 @@ static int s_test_mqtt_connection_ping_basic_scenario_fn(struct aws_allocator *a .client_id = aws_byte_cursor_from_c_str("client1234"), .host_name = aws_byte_cursor_from_c_str(state_test_data->endpoint.address), .socket_options = &state_test_data->socket_options, - .on_connection_complete = s_on_connection_complete_fn, + .on_connection_complete = aws_test311_on_connection_complete_fn, .keep_alive_time_secs = 4, .ping_timeout_ms = 100, }; ASSERT_SUCCESS(aws_mqtt_client_connection_connect(state_test_data->mqtt_connection, &connection_options)); - s_wait_for_connection_to_complete(state_test_data); + aws_test311_wait_for_connection_to_complete(state_test_data); /* PING will be in 4 seconds */ aws_thread_current_sleep(3000000000); /* Wait 3 seconds */ @@ -3771,10 +3132,10 @@ static int s_test_mqtt_connection_ping_basic_scenario_fn(struct aws_allocator *a AWS_MQTT_QOS_AT_LEAST_ONCE, false, &payload_1, - s_on_op_complete, + aws_test311_on_op_complete, state_test_data); ASSERT_TRUE(packet_id_1 > 0); - s_wait_for_ops_completed(state_test_data); + aws_test311_wait_for_ops_completed(state_test_data); /* Publish packet written at 3 seconds */ aws_thread_current_sleep(1250000000); /* Wait 1.25 second (the extra 0.25 is to account for jitter) */ @@ -3797,9 +3158,9 @@ static int s_test_mqtt_connection_ping_basic_scenario_fn(struct aws_allocator *a ASSERT_INT_EQUALS(2, mqtt_mock_server_get_ping_count(state_test_data->mock_server)); /* Disconnect and finish! */ - ASSERT_SUCCESS( - aws_mqtt_client_connection_disconnect(state_test_data->mqtt_connection, s_on_disconnect_fn, state_test_data)); - s_wait_for_disconnect_to_complete(state_test_data); + ASSERT_SUCCESS(aws_mqtt_client_connection_disconnect( + state_test_data->mqtt_connection, aws_test311_on_disconnect_fn, state_test_data)); + aws_test311_wait_for_disconnect_to_complete(state_test_data); return AWS_OP_SUCCESS; } @@ -3825,13 +3186,13 @@ static int s_test_mqtt_connection_ping_double_scenario_fn(struct aws_allocator * .client_id = aws_byte_cursor_from_c_str("client1234"), .host_name = aws_byte_cursor_from_c_str(state_test_data->endpoint.address), .socket_options = &state_test_data->socket_options, - .on_connection_complete = s_on_connection_complete_fn, + .on_connection_complete = aws_test311_on_connection_complete_fn, .keep_alive_time_secs = 4, .ping_timeout_ms = 100, }; ASSERT_SUCCESS(aws_mqtt_client_connection_connect(state_test_data->mqtt_connection, &connection_options)); - s_wait_for_connection_to_complete(state_test_data); + aws_test311_wait_for_connection_to_complete(state_test_data); /* PING will be in 4 seconds */ aws_thread_current_sleep(3000000000); /* Wait 3 seconds */ @@ -3849,10 +3210,10 @@ static int s_test_mqtt_connection_ping_double_scenario_fn(struct aws_allocator * AWS_MQTT_QOS_AT_LEAST_ONCE, false, &payload_1, - s_on_op_complete, + aws_test311_on_op_complete, state_test_data); ASSERT_TRUE(packet_id_1 > 0); - s_wait_for_ops_completed(state_test_data); + aws_test311_wait_for_ops_completed(state_test_data); /* Publish packet written at 3 seconds */ aws_thread_current_sleep(1250000000); /* Wait 1.25 second (the extra 0.25 is to account for jitter) */ @@ -3874,10 +3235,10 @@ static int s_test_mqtt_connection_ping_double_scenario_fn(struct aws_allocator * AWS_MQTT_QOS_AT_LEAST_ONCE, false, &payload_1, - s_on_op_complete, + aws_test311_on_op_complete, state_test_data); ASSERT_TRUE(packet_id_2 > 0); - s_wait_for_ops_completed(state_test_data); + aws_test311_wait_for_ops_completed(state_test_data); /* Publish packet written at 2 seconds (relative to PING that was scheduled above) */ aws_thread_current_sleep(4250000000); /* Wait 4.25 (the extra 0.25 is to account for jitter) seconds */ @@ -3895,9 +3256,9 @@ static int s_test_mqtt_connection_ping_double_scenario_fn(struct aws_allocator * */ /* Disconnect and finish! */ - ASSERT_SUCCESS( - aws_mqtt_client_connection_disconnect(state_test_data->mqtt_connection, s_on_disconnect_fn, state_test_data)); - s_wait_for_disconnect_to_complete(state_test_data); + ASSERT_SUCCESS(aws_mqtt_client_connection_disconnect( + state_test_data->mqtt_connection, aws_test311_on_disconnect_fn, state_test_data)); + aws_test311_wait_for_disconnect_to_complete(state_test_data); return AWS_OP_SUCCESS; } @@ -3918,7 +3279,7 @@ static int s_test_mqtt_connection_termination_callback_simple_fn(struct aws_allo struct mqtt_connection_state_test *state_test_data = ctx; ASSERT_SUCCESS(aws_mqtt_client_connection_set_connection_termination_handler( - state_test_data->mqtt_connection, s_on_connection_termination_fn, state_test_data)); + state_test_data->mqtt_connection, aws_test311_on_connection_termination_fn, state_test_data)); return AWS_OP_SUCCESS; } @@ -3943,11 +3304,11 @@ static int s_test_mqtt_validation_failure_publish_qos_fn(struct aws_allocator *a .client_id = aws_byte_cursor_from_c_str("client1234"), .host_name = aws_byte_cursor_from_c_str(state_test_data->endpoint.address), .socket_options = &state_test_data->socket_options, - .on_connection_complete = s_on_connection_complete_fn, + .on_connection_complete = aws_test311_on_connection_complete_fn, }; ASSERT_SUCCESS(aws_mqtt_client_connection_connect(state_test_data->mqtt_connection, &connection_options)); - s_wait_for_connection_to_complete(state_test_data); + aws_test311_wait_for_connection_to_complete(state_test_data); struct aws_byte_cursor topic = aws_byte_cursor_from_c_str("a/b"); ASSERT_INT_EQUALS( @@ -3958,14 +3319,14 @@ static int s_test_mqtt_validation_failure_publish_qos_fn(struct aws_allocator *a (enum aws_mqtt_qos)3, true, NULL, - s_on_op_complete, + aws_test311_on_op_complete, state_test_data)); int error_code = aws_last_error(); ASSERT_INT_EQUALS(AWS_ERROR_MQTT_INVALID_QOS, error_code); - ASSERT_SUCCESS( - aws_mqtt_client_connection_disconnect(state_test_data->mqtt_connection, s_on_disconnect_fn, state_test_data)); - s_wait_for_disconnect_to_complete(state_test_data); + ASSERT_SUCCESS(aws_mqtt_client_connection_disconnect( + state_test_data->mqtt_connection, aws_test311_on_disconnect_fn, state_test_data)); + aws_test311_wait_for_disconnect_to_complete(state_test_data); return AWS_OP_SUCCESS; } @@ -3990,11 +3351,11 @@ static int s_test_mqtt_validation_failure_subscribe_empty_fn(struct aws_allocato .client_id = aws_byte_cursor_from_c_str("client1234"), .host_name = aws_byte_cursor_from_c_str(state_test_data->endpoint.address), .socket_options = &state_test_data->socket_options, - .on_connection_complete = s_on_connection_complete_fn, + .on_connection_complete = aws_test311_on_connection_complete_fn, }; ASSERT_SUCCESS(aws_mqtt_client_connection_connect(state_test_data->mqtt_connection, &connection_options)); - s_wait_for_connection_to_complete(state_test_data); + aws_test311_wait_for_connection_to_complete(state_test_data); struct aws_array_list topic_filters; size_t list_len = 2; @@ -4004,13 +3365,13 @@ static int s_test_mqtt_validation_failure_subscribe_empty_fn(struct aws_allocato ASSERT_INT_EQUALS( 0, aws_mqtt_client_connection_subscribe_multiple( - state_test_data->mqtt_connection, &topic_filters, s_on_multi_suback, state_test_data)); + state_test_data->mqtt_connection, &topic_filters, aws_test311_on_multi_suback, state_test_data)); int error_code = aws_last_error(); ASSERT_INT_EQUALS(AWS_ERROR_INVALID_ARGUMENT, error_code); - ASSERT_SUCCESS( - aws_mqtt_client_connection_disconnect(state_test_data->mqtt_connection, s_on_disconnect_fn, state_test_data)); - s_wait_for_disconnect_to_complete(state_test_data); + ASSERT_SUCCESS(aws_mqtt_client_connection_disconnect( + state_test_data->mqtt_connection, aws_test311_on_disconnect_fn, state_test_data)); + aws_test311_wait_for_disconnect_to_complete(state_test_data); return AWS_OP_SUCCESS; } @@ -4035,22 +3396,22 @@ static int s_test_mqtt_validation_failure_unsubscribe_null_fn(struct aws_allocat .client_id = aws_byte_cursor_from_c_str("client1234"), .host_name = aws_byte_cursor_from_c_str(state_test_data->endpoint.address), .socket_options = &state_test_data->socket_options, - .on_connection_complete = s_on_connection_complete_fn, + .on_connection_complete = aws_test311_on_connection_complete_fn, }; ASSERT_SUCCESS(aws_mqtt_client_connection_connect(state_test_data->mqtt_connection, &connection_options)); - s_wait_for_connection_to_complete(state_test_data); + aws_test311_wait_for_connection_to_complete(state_test_data); ASSERT_INT_EQUALS( 0, aws_mqtt_client_connection_unsubscribe( - state_test_data->mqtt_connection, NULL, s_on_op_complete, state_test_data)); + state_test_data->mqtt_connection, NULL, aws_test311_on_op_complete, state_test_data)); int error_code = aws_last_error(); ASSERT_INT_EQUALS(AWS_ERROR_MQTT_INVALID_TOPIC, error_code); - ASSERT_SUCCESS( - aws_mqtt_client_connection_disconnect(state_test_data->mqtt_connection, s_on_disconnect_fn, state_test_data)); - s_wait_for_disconnect_to_complete(state_test_data); + ASSERT_SUCCESS(aws_mqtt_client_connection_disconnect( + state_test_data->mqtt_connection, aws_test311_on_disconnect_fn, state_test_data)); + aws_test311_wait_for_disconnect_to_complete(state_test_data); return AWS_OP_SUCCESS; } @@ -4078,7 +3439,7 @@ static int s_test_mqtt_validation_failure_connect_invalid_client_id_utf8_fn( .client_id = s_bad_client_id_utf8, .host_name = aws_byte_cursor_from_c_str(state_test_data->endpoint.address), .socket_options = &state_test_data->socket_options, - .on_connection_complete = s_on_connection_complete_fn, + .on_connection_complete = aws_test311_on_connection_complete_fn, }; ASSERT_FAILS(aws_mqtt_client_connection_connect(state_test_data->mqtt_connection, &connection_options)); diff --git a/tests/v3/mqtt311_listener_test.c b/tests/v3/mqtt311_listener_test.c new file mode 100644 index 00000000..758ee89f --- /dev/null +++ b/tests/v3/mqtt311_listener_test.c @@ -0,0 +1,375 @@ +/** + * Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. + * SPDX-License-Identifier: Apache-2.0. + */ + +#include + +#include + +#include "mqtt311_testing_utils.h" +#include "mqtt_mock_server_handler.h" + +#include + +struct mqtt311_listener_connection_success_record { + bool joined_session; +}; + +struct mqtt311_listener_publish_record { + struct aws_byte_buf topic; + struct aws_byte_buf payload; +}; + +struct mqtt311_listener_test_context { + struct aws_allocator *allocator; + + struct aws_mqtt311_listener *listener; + + struct mqtt_connection_state_test *mqtt311_test_context; + int mqtt311_test_context_setup_result; + + struct aws_array_list connection_success_events; + struct aws_array_list publish_events; + bool terminated; + + struct aws_mutex lock; + struct aws_condition_variable signal; +}; + +static void s_311_listener_test_on_publish_received( + struct aws_mqtt_client_connection *connection, + const struct aws_byte_cursor *topic, + const struct aws_byte_cursor *payload, + bool dup, + enum aws_mqtt_qos qos, + bool retain, + void *userdata) { + (void)connection; + (void)dup; + (void)qos; + (void)retain; + + struct mqtt311_listener_test_context *context = userdata; + + struct mqtt311_listener_publish_record publish_record; + AWS_ZERO_STRUCT(publish_record); + + aws_byte_buf_init_copy_from_cursor(&publish_record.topic, context->allocator, *topic); + aws_byte_buf_init_copy_from_cursor(&publish_record.payload, context->allocator, *payload); + + aws_mutex_lock(&context->lock); + aws_array_list_push_back(&context->publish_events, &publish_record); + aws_mutex_unlock(&context->lock); + aws_condition_variable_notify_all(&context->signal); +} + +static void s_311_listener_test_on_connection_success( + struct aws_mqtt_client_connection *connection, + enum aws_mqtt_connect_return_code return_code, + bool session_present, + void *userdata) { + (void)connection; + (void)return_code; + + struct mqtt311_listener_test_context *context = userdata; + + struct mqtt311_listener_connection_success_record connection_success_record = { + .joined_session = session_present, + }; + + aws_mutex_lock(&context->lock); + aws_array_list_push_back(&context->connection_success_events, &connection_success_record); + aws_mutex_unlock(&context->lock); + aws_condition_variable_notify_all(&context->signal); +} + +static void s_311_listener_test_on_termination(void *complete_ctx) { + struct mqtt311_listener_test_context *context = complete_ctx; + + aws_mutex_lock(&context->lock); + context->terminated = true; + aws_mutex_unlock(&context->lock); + aws_condition_variable_notify_all(&context->signal); +} + +static int mqtt311_listener_test_context_init( + struct mqtt311_listener_test_context *context, + struct aws_allocator *allocator, + struct mqtt_connection_state_test *mqtt311_test_context) { + AWS_ZERO_STRUCT(*context); + + context->allocator = allocator; + context->mqtt311_test_context = mqtt311_test_context; + + aws_array_list_init_dynamic( + &context->connection_success_events, allocator, 10, sizeof(struct mqtt311_listener_connection_success_record)); + aws_array_list_init_dynamic( + &context->publish_events, allocator, 10, sizeof(struct mqtt311_listener_publish_record)); + + aws_mutex_init(&context->lock); + aws_condition_variable_init(&context->signal); + + context->mqtt311_test_context_setup_result = aws_test311_setup_mqtt_server_fn(allocator, mqtt311_test_context); + ASSERT_SUCCESS(context->mqtt311_test_context_setup_result); + + struct aws_mqtt311_listener_config listener_config = { + .connection = mqtt311_test_context->mqtt_connection, + .listener_callbacks = + { + .publish_received_handler = s_311_listener_test_on_publish_received, + .connection_success_handler = s_311_listener_test_on_connection_success, + .user_data = context, + }, + .termination_callback = s_311_listener_test_on_termination, + .termination_callback_user_data = context, + }; + + context->listener = aws_mqtt311_listener_new(allocator, &listener_config); + + return AWS_OP_SUCCESS; +} + +static bool s_is_listener_terminated(void *userdata) { + struct mqtt311_listener_test_context *context = userdata; + + return context->terminated; +} + +static void s_wait_for_listener_termination_callback(struct mqtt311_listener_test_context *context) { + aws_mutex_lock(&context->lock); + aws_condition_variable_wait_pred(&context->signal, &context->lock, s_is_listener_terminated, context); + aws_mutex_unlock(&context->lock); +} + +static void mqtt311_listener_test_context_clean_up(struct mqtt311_listener_test_context *context) { + aws_mqtt311_listener_release(context->listener); + s_wait_for_listener_termination_callback(context); + + aws_test311_clean_up_mqtt_server_fn( + context->allocator, context->mqtt311_test_context_setup_result, context->mqtt311_test_context); + + aws_mutex_clean_up(&context->lock); + aws_condition_variable_clean_up(&context->signal); + + aws_array_list_clean_up(&context->connection_success_events); + + for (size_t i = 0; i < aws_array_list_length(&context->publish_events); ++i) { + struct mqtt311_listener_publish_record publish_record; + AWS_ZERO_STRUCT(publish_record); + + aws_array_list_get_at(&context->publish_events, &publish_record, i); + + aws_byte_buf_clean_up(&publish_record.topic); + aws_byte_buf_clean_up(&publish_record.payload); + } + + aws_array_list_clean_up(&context->publish_events); +} + +struct connection_success_event_test_context { + struct mqtt311_listener_test_context *context; + bool joined_session; + size_t expected_count; +}; + +static bool s_contains_connection_success_events(void *userdata) { + struct connection_success_event_test_context *wait_context = userdata; + struct mqtt311_listener_test_context *context = wait_context->context; + + size_t found = 0; + for (size_t i = 0; i < aws_array_list_length(&context->connection_success_events); ++i) { + struct mqtt311_listener_connection_success_record record; + aws_array_list_get_at(&context->connection_success_events, &record, i); + + if (record.joined_session == wait_context->joined_session) { + ++found; + } + } + + return found >= wait_context->expected_count; +} + +static void s_wait_for_connection_success_events( + struct mqtt311_listener_test_context *context, + bool joined_session, + size_t expected_count) { + struct connection_success_event_test_context wait_context = { + .context = context, + .joined_session = joined_session, + .expected_count = expected_count, + }; + + aws_mutex_lock(&context->lock); + aws_condition_variable_wait_pred( + &context->signal, &context->lock, s_contains_connection_success_events, &wait_context); + aws_mutex_unlock(&context->lock); +} + +static int s_do_mqtt311_listener_connection_success_event_test(struct aws_allocator *allocator, bool session_present) { + aws_mqtt_library_init(allocator); + + struct mqtt_connection_state_test mqtt311_context; + AWS_ZERO_STRUCT(mqtt311_context); + + struct mqtt311_listener_test_context test_context; + ASSERT_SUCCESS(mqtt311_listener_test_context_init(&test_context, allocator, &mqtt311_context)); + + mqtt_mock_server_set_session_present(mqtt311_context.mock_server, session_present); + + struct aws_mqtt_connection_options connection_options = { + .user_data = &mqtt311_context, + .clean_session = true, + .client_id = aws_byte_cursor_from_c_str("client1234"), + .host_name = aws_byte_cursor_from_c_str(mqtt311_context.endpoint.address), + .socket_options = &mqtt311_context.socket_options, + .on_connection_complete = aws_test311_on_connection_complete_fn, + }; + + ASSERT_SUCCESS(aws_mqtt_client_connection_connect(mqtt311_context.mqtt_connection, &connection_options)); + aws_test311_wait_for_connection_to_complete(&mqtt311_context); + + s_wait_for_connection_success_events(&test_context, session_present, 1); + + ASSERT_SUCCESS(aws_mqtt_client_connection_disconnect( + mqtt311_context.mqtt_connection, aws_test311_on_disconnect_fn, &mqtt311_context)); + aws_test311_wait_for_disconnect_to_complete(&mqtt311_context); + + ASSERT_SUCCESS(aws_mqtt_client_connection_connect(mqtt311_context.mqtt_connection, &connection_options)); + aws_test311_wait_for_connection_to_complete(&mqtt311_context); + + s_wait_for_connection_success_events(&test_context, session_present, 2); + + ASSERT_SUCCESS(aws_mqtt_client_connection_disconnect( + mqtt311_context.mqtt_connection, aws_test311_on_disconnect_fn, &mqtt311_context)); + aws_test311_wait_for_disconnect_to_complete(&mqtt311_context); + + mqtt311_listener_test_context_clean_up(&test_context); + + aws_mqtt_library_clean_up(); + + return AWS_OP_SUCCESS; +} + +static int s_mqtt311_listener_connection_success_event_no_session_fn(struct aws_allocator *allocator, void *ctx) { + (void)ctx; + + return s_do_mqtt311_listener_connection_success_event_test(allocator, false); +} + +AWS_TEST_CASE( + mqtt311_listener_connection_success_event_no_session, + s_mqtt311_listener_connection_success_event_no_session_fn) + +static int s_mqtt311_listener_connection_success_event_with_session_fn(struct aws_allocator *allocator, void *ctx) { + (void)ctx; + + return s_do_mqtt311_listener_connection_success_event_test(allocator, true); +} + +AWS_TEST_CASE( + mqtt311_listener_connection_success_event_with_session, + s_mqtt311_listener_connection_success_event_with_session_fn) + +struct publish_event_test_context { + struct mqtt311_listener_test_context *context; + struct aws_byte_cursor expected_topic; + struct aws_byte_cursor expected_payload; + size_t expected_count; +}; + +static bool s_contains_publish_events(void *userdata) { + struct publish_event_test_context *wait_context = userdata; + struct mqtt311_listener_test_context *context = wait_context->context; + + size_t found = 0; + for (size_t i = 0; i < aws_array_list_length(&context->publish_events); ++i) { + struct mqtt311_listener_publish_record record; + aws_array_list_get_at(&context->publish_events, &record, i); + + struct aws_byte_cursor actual_topic = aws_byte_cursor_from_buf(&record.topic); + if (!aws_byte_cursor_eq(&wait_context->expected_topic, &actual_topic)) { + continue; + } + + struct aws_byte_cursor actual_payload = aws_byte_cursor_from_buf(&record.payload); + if (!aws_byte_cursor_eq(&wait_context->expected_payload, &actual_payload)) { + continue; + } + + ++found; + } + + return found >= wait_context->expected_count; +} + +static void s_wait_for_publish_events( + struct mqtt311_listener_test_context *context, + struct aws_byte_cursor topic, + struct aws_byte_cursor payload, + size_t expected_count) { + struct publish_event_test_context wait_context = { + .context = context, + .expected_topic = topic, + .expected_payload = payload, + .expected_count = expected_count, + }; + + aws_mutex_lock(&context->lock); + aws_condition_variable_wait_pred(&context->signal, &context->lock, s_contains_publish_events, &wait_context); + aws_mutex_unlock(&context->lock); +} + +static int s_mqtt311_listener_publish_event_fn(struct aws_allocator *allocator, void *ctx) { + (void)ctx; + + aws_mqtt_library_init(allocator); + + struct mqtt_connection_state_test mqtt311_context; + AWS_ZERO_STRUCT(mqtt311_context); + + struct mqtt311_listener_test_context test_context; + ASSERT_SUCCESS(mqtt311_listener_test_context_init(&test_context, allocator, &mqtt311_context)); + + mqtt_mock_server_set_publish_reflection(mqtt311_context.mock_server, true); + + struct aws_mqtt_connection_options connection_options = { + .user_data = &mqtt311_context, + .clean_session = true, + .client_id = aws_byte_cursor_from_c_str("client1234"), + .host_name = aws_byte_cursor_from_c_str(mqtt311_context.endpoint.address), + .socket_options = &mqtt311_context.socket_options, + .on_connection_complete = aws_test311_on_connection_complete_fn, + }; + + ASSERT_SUCCESS(aws_mqtt_client_connection_connect(mqtt311_context.mqtt_connection, &connection_options)); + aws_test311_wait_for_connection_to_complete(&mqtt311_context); + + struct aws_byte_cursor topic1 = aws_byte_cursor_from_c_str("hello/world/1"); + struct aws_byte_cursor payload1 = aws_byte_cursor_from_c_str("payload1"); + aws_mqtt_client_connection_publish( + mqtt311_context.mqtt_connection, &topic1, AWS_MQTT_QOS_AT_LEAST_ONCE, false, &payload1, NULL, NULL); + + s_wait_for_publish_events(&test_context, topic1, payload1, 1); + + struct aws_byte_cursor topic2 = aws_byte_cursor_from_c_str("nothing/important"); + struct aws_byte_cursor payload2 = aws_byte_cursor_from_c_str("somethingneeddoing?"); + aws_mqtt_client_connection_publish( + mqtt311_context.mqtt_connection, &topic2, AWS_MQTT_QOS_AT_LEAST_ONCE, false, &payload2, NULL, NULL); + aws_mqtt_client_connection_publish( + mqtt311_context.mqtt_connection, &topic2, AWS_MQTT_QOS_AT_LEAST_ONCE, false, &payload2, NULL, NULL); + + s_wait_for_publish_events(&test_context, topic2, payload2, 2); + + ASSERT_SUCCESS(aws_mqtt_client_connection_disconnect( + mqtt311_context.mqtt_connection, aws_test311_on_disconnect_fn, &mqtt311_context)); + aws_test311_wait_for_disconnect_to_complete(&mqtt311_context); + + mqtt311_listener_test_context_clean_up(&test_context); + + aws_mqtt_library_clean_up(); + + return AWS_OP_SUCCESS; +} + +AWS_TEST_CASE(mqtt311_listener_publish_event, s_mqtt311_listener_publish_event_fn) \ No newline at end of file diff --git a/tests/v3/mqtt311_testing_utils.c b/tests/v3/mqtt311_testing_utils.c new file mode 100644 index 00000000..52a55e92 --- /dev/null +++ b/tests/v3/mqtt311_testing_utils.c @@ -0,0 +1,580 @@ +/** + * Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. + * SPDX-License-Identifier: Apache-2.0. + */ + +#include "mqtt311_testing_utils.h" + +#include +#include +#include +#include +#include +#include + +#include "mqtt_mock_server_handler.h" + +static void s_on_incoming_channel_setup_fn( + struct aws_server_bootstrap *bootstrap, + int error_code, + struct aws_channel *channel, + void *user_data) { + (void)bootstrap; + struct mqtt_connection_state_test *state_test_data = user_data; + + state_test_data->error = error_code; + + if (!error_code) { + aws_mutex_lock(&state_test_data->lock); + state_test_data->server_disconnect_completed = false; + aws_mutex_unlock(&state_test_data->lock); + AWS_LOGF_DEBUG(TEST_LOG_SUBJECT, "server channel setup completed"); + + state_test_data->server_channel = channel; + struct aws_channel_slot *test_handler_slot = aws_channel_slot_new(channel); + aws_channel_slot_insert_end(channel, test_handler_slot); + mqtt_mock_server_handler_update_slot(state_test_data->mock_server, test_handler_slot); + aws_channel_slot_set_handler(test_handler_slot, state_test_data->mock_server); + } +} + +static void s_on_incoming_channel_shutdown_fn( + struct aws_server_bootstrap *bootstrap, + int error_code, + struct aws_channel *channel, + void *user_data) { + (void)bootstrap; + (void)error_code; + (void)channel; + struct mqtt_connection_state_test *state_test_data = user_data; + aws_mutex_lock(&state_test_data->lock); + state_test_data->server_disconnect_completed = true; + AWS_LOGF_DEBUG(TEST_LOG_SUBJECT, "server channel shutdown completed"); + aws_mutex_unlock(&state_test_data->lock); + aws_condition_variable_notify_one(&state_test_data->cvar); +} + +static void s_on_listener_destroy(struct aws_server_bootstrap *bootstrap, void *user_data) { + (void)bootstrap; + struct mqtt_connection_state_test *state_test_data = user_data; + aws_mutex_lock(&state_test_data->lock); + state_test_data->listener_destroyed = true; + aws_mutex_unlock(&state_test_data->lock); + aws_condition_variable_notify_one(&state_test_data->cvar); +} + +static bool s_is_listener_destroyed(void *arg) { + struct mqtt_connection_state_test *state_test_data = arg; + return state_test_data->listener_destroyed; +} + +static void s_wait_on_listener_cleanup(struct mqtt_connection_state_test *state_test_data) { + aws_mutex_lock(&state_test_data->lock); + aws_condition_variable_wait_pred( + &state_test_data->cvar, &state_test_data->lock, s_is_listener_destroyed, state_test_data); + aws_mutex_unlock(&state_test_data->lock); +} + +static void s_on_connection_interrupted(struct aws_mqtt_client_connection *connection, int error_code, void *userdata) { + (void)connection; + (void)error_code; + struct mqtt_connection_state_test *state_test_data = userdata; + + aws_mutex_lock(&state_test_data->lock); + state_test_data->connection_interrupted = true; + state_test_data->interruption_error = error_code; + aws_mutex_unlock(&state_test_data->lock); + AWS_LOGF_DEBUG(TEST_LOG_SUBJECT, "connection interrupted"); + aws_condition_variable_notify_one(&state_test_data->cvar); +} + +static bool s_is_connection_interrupted(void *arg) { + struct mqtt_connection_state_test *state_test_data = arg; + return state_test_data->connection_interrupted; +} + +void aws_test311_wait_for_interrupt_to_complete(struct mqtt_connection_state_test *state_test_data) { + aws_mutex_lock(&state_test_data->lock); + aws_condition_variable_wait_pred( + &state_test_data->cvar, &state_test_data->lock, s_is_connection_interrupted, state_test_data); + state_test_data->connection_interrupted = false; + aws_mutex_unlock(&state_test_data->lock); +} + +static void s_on_connection_resumed( + struct aws_mqtt_client_connection *connection, + enum aws_mqtt_connect_return_code return_code, + bool session_present, + void *userdata) { + (void)connection; + (void)return_code; + (void)session_present; + AWS_LOGF_DEBUG(TEST_LOG_SUBJECT, "reconnect completed"); + + struct mqtt_connection_state_test *state_test_data = userdata; + + aws_mutex_lock(&state_test_data->lock); + state_test_data->connection_resumed = true; + aws_mutex_unlock(&state_test_data->lock); + aws_condition_variable_notify_one(&state_test_data->cvar); +} + +static bool s_is_connection_resumed(void *arg) { + struct mqtt_connection_state_test *state_test_data = arg; + return state_test_data->connection_resumed; +} + +void aws_test311_wait_for_reconnect_to_complete(struct mqtt_connection_state_test *state_test_data) { + aws_mutex_lock(&state_test_data->lock); + aws_condition_variable_wait_pred( + &state_test_data->cvar, &state_test_data->lock, s_is_connection_resumed, state_test_data); + state_test_data->connection_resumed = false; + aws_mutex_unlock(&state_test_data->lock); +} + +static void s_on_connection_success( + struct aws_mqtt_client_connection *connection, + enum aws_mqtt_connect_return_code return_code, + bool session_present, + void *userdata) { + (void)connection; + struct mqtt_connection_state_test *state_test_data = userdata; + aws_mutex_lock(&state_test_data->lock); + + state_test_data->session_present = session_present; + state_test_data->mqtt_return_code = return_code; + state_test_data->connection_success = true; + aws_mutex_unlock(&state_test_data->lock); + + aws_condition_variable_notify_one(&state_test_data->cvar); +} + +static void s_on_connection_failure(struct aws_mqtt_client_connection *connection, int error_code, void *userdata) { + (void)connection; + struct mqtt_connection_state_test *state_test_data = userdata; + aws_mutex_lock(&state_test_data->lock); + + state_test_data->error = error_code; + state_test_data->connection_failure = true; + aws_mutex_unlock(&state_test_data->lock); + + aws_condition_variable_notify_one(&state_test_data->cvar); +} + +static bool s_is_connection_succeed(void *arg) { + struct mqtt_connection_state_test *state_test_data = arg; + return state_test_data->connection_success; +} + +static bool s_is_connection_failed(void *arg) { + struct mqtt_connection_state_test *state_test_data = arg; + return state_test_data->connection_failure; +} + +void aws_test311_wait_for_connection_to_succeed(struct mqtt_connection_state_test *state_test_data) { + aws_mutex_lock(&state_test_data->lock); + aws_condition_variable_wait_pred( + &state_test_data->cvar, &state_test_data->lock, s_is_connection_succeed, state_test_data); + state_test_data->connection_success = false; + aws_mutex_unlock(&state_test_data->lock); +} + +void aws_test311_wait_for_connection_to_fail(struct mqtt_connection_state_test *state_test_data) { + aws_mutex_lock(&state_test_data->lock); + aws_condition_variable_wait_pred( + &state_test_data->cvar, &state_test_data->lock, s_is_connection_failed, state_test_data); + state_test_data->connection_failure = false; + aws_mutex_unlock(&state_test_data->lock); +} + +static bool s_is_termination_completed(void *arg) { + struct mqtt_connection_state_test *state_test_data = arg; + return state_test_data->connection_terminated; +} + +static void s_wait_for_termination_to_complete(struct mqtt_connection_state_test *state_test_data) { + aws_mutex_lock(&state_test_data->lock); + aws_condition_variable_wait_pred( + &state_test_data->cvar, &state_test_data->lock, s_is_termination_completed, state_test_data); + state_test_data->connection_terminated = false; + aws_mutex_unlock(&state_test_data->lock); +} + +void aws_test311_on_connection_termination_fn(void *userdata) { + struct mqtt_connection_state_test *state_test_data = (struct mqtt_connection_state_test *)userdata; + + aws_mutex_lock(&state_test_data->lock); + state_test_data->connection_termination_calls += 1; + state_test_data->connection_terminated = true; + aws_mutex_unlock(&state_test_data->lock); + aws_condition_variable_notify_one(&state_test_data->cvar); +} + +static void s_on_any_publish_received( + struct aws_mqtt_client_connection *connection, + const struct aws_byte_cursor *topic, + const struct aws_byte_cursor *payload, + bool dup, + enum aws_mqtt_qos qos, + bool retain, + void *userdata) { + (void)connection; + struct mqtt_connection_state_test *state_test_data = userdata; + + struct aws_byte_buf payload_cp; + aws_byte_buf_init_copy_from_cursor(&payload_cp, state_test_data->allocator, *payload); + struct aws_byte_buf topic_cp; + aws_byte_buf_init_copy_from_cursor(&topic_cp, state_test_data->allocator, *topic); + struct received_publish_packet received_packet = { + .payload = payload_cp, + .topic = topic_cp, + .dup = dup, + .qos = qos, + .retain = retain, + }; + + aws_mutex_lock(&state_test_data->lock); + aws_array_list_push_back(&state_test_data->any_published_messages, &received_packet); + state_test_data->any_publishes_received++; + aws_mutex_unlock(&state_test_data->lock); + aws_condition_variable_notify_one(&state_test_data->cvar); +} + +/** + * sets up a unix domain socket server and socket options. Creates an mqtt connection configured to use + * the domain socket. + */ +int aws_test311_setup_mqtt_server_fn(struct aws_allocator *allocator, void *ctx) { + aws_mqtt_library_init(allocator); + + struct mqtt_connection_state_test *state_test_data = ctx; + + AWS_ZERO_STRUCT(*state_test_data); + + state_test_data->allocator = allocator; + state_test_data->el_group = aws_event_loop_group_new_default(allocator, 1, NULL); + + state_test_data->mock_server = new_mqtt_mock_server(allocator); + ASSERT_NOT_NULL(state_test_data->mock_server); + + state_test_data->server_bootstrap = aws_server_bootstrap_new(allocator, state_test_data->el_group); + ASSERT_NOT_NULL(state_test_data->server_bootstrap); + + struct aws_socket_options socket_options = { + .connect_timeout_ms = 100, + .domain = AWS_SOCKET_LOCAL, + }; + + state_test_data->socket_options = socket_options; + ASSERT_SUCCESS(aws_condition_variable_init(&state_test_data->cvar)); + ASSERT_SUCCESS(aws_mutex_init(&state_test_data->lock)); + + aws_socket_endpoint_init_local_address_for_test(&state_test_data->endpoint); + + struct aws_server_socket_channel_bootstrap_options server_bootstrap_options = { + .bootstrap = state_test_data->server_bootstrap, + .host_name = state_test_data->endpoint.address, + .port = state_test_data->endpoint.port, + .socket_options = &state_test_data->socket_options, + .incoming_callback = s_on_incoming_channel_setup_fn, + .shutdown_callback = s_on_incoming_channel_shutdown_fn, + .destroy_callback = s_on_listener_destroy, + .user_data = state_test_data, + }; + state_test_data->listener = aws_server_bootstrap_new_socket_listener(&server_bootstrap_options); + + ASSERT_NOT_NULL(state_test_data->listener); + + struct aws_host_resolver_default_options resolver_options = { + .el_group = state_test_data->el_group, + .max_entries = 1, + }; + state_test_data->host_resolver = aws_host_resolver_new_default(allocator, &resolver_options); + + struct aws_client_bootstrap_options bootstrap_options = { + .event_loop_group = state_test_data->el_group, + .user_data = state_test_data, + .host_resolver = state_test_data->host_resolver, + }; + + state_test_data->client_bootstrap = aws_client_bootstrap_new(allocator, &bootstrap_options); + + state_test_data->mqtt_client = aws_mqtt_client_new(allocator, state_test_data->client_bootstrap); + state_test_data->mqtt_connection = aws_mqtt_client_connection_new(state_test_data->mqtt_client); + ASSERT_NOT_NULL(state_test_data->mqtt_connection); + + ASSERT_SUCCESS(aws_mqtt_client_connection_set_connection_interruption_handlers( + state_test_data->mqtt_connection, + s_on_connection_interrupted, + state_test_data, + s_on_connection_resumed, + state_test_data)); + + ASSERT_SUCCESS(aws_mqtt_client_connection_set_connection_result_handlers( + state_test_data->mqtt_connection, + s_on_connection_success, + state_test_data, + s_on_connection_failure, + state_test_data)); + + ASSERT_SUCCESS(aws_mqtt_client_connection_set_on_any_publish_handler( + state_test_data->mqtt_connection, s_on_any_publish_received, state_test_data)); + + ASSERT_SUCCESS(aws_array_list_init_dynamic( + &state_test_data->published_messages, allocator, 4, sizeof(struct received_publish_packet))); + ASSERT_SUCCESS(aws_array_list_init_dynamic( + &state_test_data->any_published_messages, allocator, 4, sizeof(struct received_publish_packet))); + ASSERT_SUCCESS(aws_array_list_init_dynamic(&state_test_data->qos_returned, allocator, 2, sizeof(uint8_t))); + + ASSERT_SUCCESS(aws_mqtt_client_connection_set_connection_termination_handler( + state_test_data->mqtt_connection, aws_test311_on_connection_termination_fn, state_test_data)); + + return AWS_OP_SUCCESS; +} + +static void s_received_publish_packet_list_clean_up(struct aws_array_list *list) { + for (size_t i = 0; i < aws_array_list_length(list); ++i) { + struct received_publish_packet *val_ptr = NULL; + aws_array_list_get_at_ptr(list, (void **)&val_ptr, i); + aws_byte_buf_clean_up(&val_ptr->payload); + aws_byte_buf_clean_up(&val_ptr->topic); + } + aws_array_list_clean_up(list); +} + +int aws_test311_clean_up_mqtt_server_fn(struct aws_allocator *allocator, int setup_result, void *ctx) { + (void)allocator; + + if (!setup_result) { + struct mqtt_connection_state_test *state_test_data = ctx; + + s_received_publish_packet_list_clean_up(&state_test_data->published_messages); + s_received_publish_packet_list_clean_up(&state_test_data->any_published_messages); + aws_array_list_clean_up(&state_test_data->qos_returned); + aws_mqtt_client_connection_release(state_test_data->mqtt_connection); + + s_wait_for_termination_to_complete(state_test_data); + ASSERT_UINT_EQUALS(1, state_test_data->connection_termination_calls); + + aws_mqtt_client_release(state_test_data->mqtt_client); + aws_client_bootstrap_release(state_test_data->client_bootstrap); + aws_host_resolver_release(state_test_data->host_resolver); + aws_server_bootstrap_destroy_socket_listener(state_test_data->server_bootstrap, state_test_data->listener); + s_wait_on_listener_cleanup(state_test_data); + aws_server_bootstrap_release(state_test_data->server_bootstrap); + aws_event_loop_group_release(state_test_data->el_group); + destroy_mqtt_mock_server(state_test_data->mock_server); + } + + aws_mqtt_library_clean_up(); + return AWS_OP_SUCCESS; +} + +void aws_test311_on_connection_complete_fn( + struct aws_mqtt_client_connection *connection, + int error_code, + enum aws_mqtt_connect_return_code return_code, + bool session_present, + void *userdata) { + (void)connection; + struct mqtt_connection_state_test *state_test_data = userdata; + aws_mutex_lock(&state_test_data->lock); + + state_test_data->session_present = session_present; + state_test_data->mqtt_return_code = return_code; + state_test_data->error = error_code; + state_test_data->connection_completed = true; + aws_mutex_unlock(&state_test_data->lock); + + aws_condition_variable_notify_one(&state_test_data->cvar); +} + +static bool s_is_connection_completed(void *arg) { + struct mqtt_connection_state_test *state_test_data = arg; + return state_test_data->connection_completed; +} + +void aws_test311_wait_for_connection_to_complete(struct mqtt_connection_state_test *state_test_data) { + aws_mutex_lock(&state_test_data->lock); + aws_condition_variable_wait_pred( + &state_test_data->cvar, &state_test_data->lock, s_is_connection_completed, state_test_data); + state_test_data->connection_completed = false; + aws_mutex_unlock(&state_test_data->lock); +} + +void aws_test311_on_disconnect_fn(struct aws_mqtt_client_connection *connection, void *userdata) { + (void)connection; + struct mqtt_connection_state_test *state_test_data = userdata; + AWS_LOGF_DEBUG(TEST_LOG_SUBJECT, "disconnect completed"); + aws_mutex_lock(&state_test_data->lock); + state_test_data->client_disconnect_completed = true; + aws_mutex_unlock(&state_test_data->lock); + + aws_condition_variable_notify_one(&state_test_data->cvar); +} + +static bool s_is_disconnect_completed(void *arg) { + struct mqtt_connection_state_test *state_test_data = arg; + return state_test_data->client_disconnect_completed && state_test_data->server_disconnect_completed; +} + +void aws_test311_wait_for_disconnect_to_complete(struct mqtt_connection_state_test *state_test_data) { + aws_mutex_lock(&state_test_data->lock); + aws_condition_variable_wait_pred( + &state_test_data->cvar, &state_test_data->lock, s_is_disconnect_completed, state_test_data); + state_test_data->client_disconnect_completed = false; + state_test_data->server_disconnect_completed = false; + aws_mutex_unlock(&state_test_data->lock); +} + +static bool s_is_any_publish_received(void *arg) { + struct mqtt_connection_state_test *state_test_data = arg; + return state_test_data->any_publishes_received == state_test_data->expected_any_publishes; +} + +void aws_test311_wait_for_any_publish(struct mqtt_connection_state_test *state_test_data) { + aws_mutex_lock(&state_test_data->lock); + aws_condition_variable_wait_pred( + &state_test_data->cvar, &state_test_data->lock, s_is_any_publish_received, state_test_data); + state_test_data->any_publishes_received = 0; + state_test_data->expected_any_publishes = 0; + aws_mutex_unlock(&state_test_data->lock); +} + +void aws_test311_on_publish_received( + struct aws_mqtt_client_connection *connection, + const struct aws_byte_cursor *topic, + const struct aws_byte_cursor *payload, + bool dup, + enum aws_mqtt_qos qos, + bool retain, + void *userdata) { + + (void)connection; + (void)topic; + struct mqtt_connection_state_test *state_test_data = userdata; + + struct aws_byte_buf payload_cp; + aws_byte_buf_init_copy_from_cursor(&payload_cp, state_test_data->allocator, *payload); + struct aws_byte_buf topic_cp; + aws_byte_buf_init_copy_from_cursor(&topic_cp, state_test_data->allocator, *topic); + struct received_publish_packet received_packet = { + .payload = payload_cp, + .topic = topic_cp, + .dup = dup, + .qos = qos, + .retain = retain, + }; + + aws_mutex_lock(&state_test_data->lock); + aws_array_list_push_back(&state_test_data->published_messages, &received_packet); + state_test_data->publishes_received++; + aws_mutex_unlock(&state_test_data->lock); + aws_condition_variable_notify_one(&state_test_data->cvar); +} + +static bool s_is_publish_received(void *arg) { + struct mqtt_connection_state_test *state_test_data = arg; + return state_test_data->publishes_received == state_test_data->expected_publishes; +} + +void aws_test311_wait_for_publish(struct mqtt_connection_state_test *state_test_data) { + aws_mutex_lock(&state_test_data->lock); + aws_condition_variable_wait_pred( + &state_test_data->cvar, &state_test_data->lock, s_is_publish_received, state_test_data); + state_test_data->publishes_received = 0; + state_test_data->expected_publishes = 0; + aws_mutex_unlock(&state_test_data->lock); +} + +void aws_test311_on_suback( + struct aws_mqtt_client_connection *connection, + uint16_t packet_id, + const struct aws_byte_cursor *topic, + enum aws_mqtt_qos qos, + int error_code, + void *userdata) { + (void)connection; + (void)packet_id; + (void)topic; + + struct mqtt_connection_state_test *state_test_data = userdata; + + aws_mutex_lock(&state_test_data->lock); + if (!error_code) { + aws_array_list_push_back(&state_test_data->qos_returned, &qos); + } + state_test_data->subscribe_completed = true; + state_test_data->subscribe_complete_error = error_code; + aws_mutex_unlock(&state_test_data->lock); + aws_condition_variable_notify_one(&state_test_data->cvar); +} + +static bool s_is_subscribe_completed(void *arg) { + struct mqtt_connection_state_test *state_test_data = arg; + return state_test_data->subscribe_completed; +} + +void aws_test311_wait_for_subscribe_to_complete(struct mqtt_connection_state_test *state_test_data) { + aws_mutex_lock(&state_test_data->lock); + aws_condition_variable_wait_pred( + &state_test_data->cvar, &state_test_data->lock, s_is_subscribe_completed, state_test_data); + state_test_data->subscribe_completed = false; + aws_mutex_unlock(&state_test_data->lock); +} + +void aws_test311_on_multi_suback( + struct aws_mqtt_client_connection *connection, + uint16_t packet_id, + const struct aws_array_list *topic_subacks, /* contains aws_mqtt_topic_subscription pointers */ + int error_code, + void *userdata) { + (void)connection; + (void)packet_id; + (void)topic_subacks; + (void)error_code; + + struct mqtt_connection_state_test *state_test_data = userdata; + + aws_mutex_lock(&state_test_data->lock); + state_test_data->subscribe_completed = true; + if (!error_code) { + size_t length = aws_array_list_length(topic_subacks); + for (size_t i = 0; i < length; ++i) { + struct aws_mqtt_topic_subscription *subscription = NULL; + aws_array_list_get_at(topic_subacks, &subscription, i); + aws_array_list_push_back(&state_test_data->qos_returned, &subscription->qos); + } + } + aws_mutex_unlock(&state_test_data->lock); + aws_condition_variable_notify_one(&state_test_data->cvar); +} + +void aws_test311_on_op_complete( + struct aws_mqtt_client_connection *connection, + uint16_t packet_id, + int error_code, + void *userdata) { + (void)connection; + (void)packet_id; + + struct mqtt_connection_state_test *state_test_data = userdata; + AWS_LOGF_DEBUG(TEST_LOG_SUBJECT, "pub op completed"); + aws_mutex_lock(&state_test_data->lock); + state_test_data->ops_completed++; + state_test_data->op_complete_error = error_code; + aws_mutex_unlock(&state_test_data->lock); + aws_condition_variable_notify_one(&state_test_data->cvar); +} + +static bool s_is_ops_completed(void *arg) { + struct mqtt_connection_state_test *state_test_data = arg; + return state_test_data->ops_completed == state_test_data->expected_ops_completed; +} + +void aws_test311_wait_for_ops_completed(struct mqtt_connection_state_test *state_test_data) { + aws_mutex_lock(&state_test_data->lock); + aws_condition_variable_wait_for_pred( + &state_test_data->cvar, &state_test_data->lock, 10000000000, s_is_ops_completed, state_test_data); + aws_mutex_unlock(&state_test_data->lock); +} \ No newline at end of file diff --git a/tests/v3/mqtt311_testing_utils.h b/tests/v3/mqtt311_testing_utils.h new file mode 100644 index 00000000..e9162c20 --- /dev/null +++ b/tests/v3/mqtt311_testing_utils.h @@ -0,0 +1,155 @@ +/** + * Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. + * SPDX-License-Identifier: Apache-2.0. + */ + +#ifndef AWS_C_MQTT_MQTT311_TESTING_UTILS_H +#define AWS_C_MQTT_MQTT311_TESTING_UTILS_H + +#include + +#include +#include +#include +#include +#include + +#include + +#define TEST_LOG_SUBJECT 60000 +#define ONE_SEC 1000000000 +// The value is extract from aws-c-mqtt/source/client.c +#define AWS_RESET_RECONNECT_BACKOFF_DELAY_SECONDS 10 +#define RECONNECT_BACKOFF_DELAY_ERROR_MARGIN_NANO_SECONDS 500000000 +#define DEFAULT_MIN_RECONNECT_DELAY_SECONDS 1 + +#define DEFAULT_TEST_PING_TIMEOUT_MS 1000 +#define DEFAULT_TEST_KEEP_ALIVE_S 2 + +struct received_publish_packet { + struct aws_byte_buf topic; + struct aws_byte_buf payload; + bool dup; + enum aws_mqtt_qos qos; + bool retain; +}; + +struct mqtt_connection_state_test { + struct aws_allocator *allocator; + struct aws_channel *server_channel; + struct aws_channel_handler *mock_server; + struct aws_client_bootstrap *client_bootstrap; + struct aws_server_bootstrap *server_bootstrap; + struct aws_event_loop_group *el_group; + struct aws_host_resolver *host_resolver; + struct aws_socket_endpoint endpoint; + struct aws_socket *listener; + struct aws_mqtt_client *mqtt_client; + struct aws_mqtt_client_connection *mqtt_connection; + struct aws_socket_options socket_options; + + bool session_present; + bool connection_completed; + bool connection_success; + bool connection_failure; + bool client_disconnect_completed; + bool server_disconnect_completed; + bool connection_interrupted; + bool connection_resumed; + bool subscribe_completed; + bool listener_destroyed; + bool connection_terminated; + int interruption_error; + int subscribe_complete_error; + int op_complete_error; + enum aws_mqtt_connect_return_code mqtt_return_code; + int error; + struct aws_condition_variable cvar; + struct aws_mutex lock; + /* any published messages from mock server, that you may not subscribe to. (Which should not happen in real life) */ + struct aws_array_list any_published_messages; /* list of struct received_publish_packet */ + size_t any_publishes_received; + size_t expected_any_publishes; + /* the published messages from mock server, that you did subscribe to. */ + struct aws_array_list published_messages; /* list of struct received_publish_packet */ + size_t publishes_received; + size_t expected_publishes; + /* The returned QoS from mock server */ + struct aws_array_list qos_returned; /* list of uint_8 */ + size_t ops_completed; + size_t expected_ops_completed; + size_t connection_close_calls; /* All of the times on_connection_closed has been called */ + + size_t connection_termination_calls; /* How many times on_connection_termination has been called, should be 1 */ +}; + +AWS_EXTERN_C_BEGIN + +int aws_test311_setup_mqtt_server_fn(struct aws_allocator *allocator, void *ctx); + +int aws_test311_clean_up_mqtt_server_fn(struct aws_allocator *allocator, int setup_result, void *ctx); + +void aws_test311_wait_for_interrupt_to_complete(struct mqtt_connection_state_test *state_test_data); + +void aws_test311_wait_for_reconnect_to_complete(struct mqtt_connection_state_test *state_test_data); + +void aws_test311_wait_for_connection_to_succeed(struct mqtt_connection_state_test *state_test_data); + +void aws_test311_wait_for_connection_to_fail(struct mqtt_connection_state_test *state_test_data); + +void aws_test311_on_connection_complete_fn( + struct aws_mqtt_client_connection *connection, + int error_code, + enum aws_mqtt_connect_return_code return_code, + bool session_present, + void *userdata); + +void aws_test311_wait_for_connection_to_complete(struct mqtt_connection_state_test *state_test_data); + +void aws_test311_on_disconnect_fn(struct aws_mqtt_client_connection *connection, void *userdata); + +void aws_test311_wait_for_disconnect_to_complete(struct mqtt_connection_state_test *state_test_data); + +void aws_test311_wait_for_any_publish(struct mqtt_connection_state_test *state_test_data); + +void aws_test311_on_publish_received( + struct aws_mqtt_client_connection *connection, + const struct aws_byte_cursor *topic, + const struct aws_byte_cursor *payload, + bool dup, + enum aws_mqtt_qos qos, + bool retain, + void *userdata); + +void aws_test311_wait_for_publish(struct mqtt_connection_state_test *state_test_data); + +void aws_test311_on_suback( + struct aws_mqtt_client_connection *connection, + uint16_t packet_id, + const struct aws_byte_cursor *topic, + enum aws_mqtt_qos qos, + int error_code, + void *userdata); + +void aws_test311_wait_for_subscribe_to_complete(struct mqtt_connection_state_test *state_test_data); + +void aws_test311_on_multi_suback( + struct aws_mqtt_client_connection *connection, + uint16_t packet_id, + const struct aws_array_list *topic_subacks, + int error_code, + void *userdata); + +void aws_test311_on_op_complete( + struct aws_mqtt_client_connection *connection, + uint16_t packet_id, + int error_code, + void *userdata); + +void aws_test311_wait_for_ops_completed(struct mqtt_connection_state_test *state_test_data); + +void aws_test311_on_connection_termination_fn(void *userdata); + +AWS_EXTERN_C_END + +#endif // AWS_C_MQTT_MQTT311_TESTING_UTILS_H diff --git a/tests/v3/mqtt_mock_server_handler.c b/tests/v3/mqtt_mock_server_handler.c index 73576a7d..0e770690 100644 --- a/tests/v3/mqtt_mock_server_handler.c +++ b/tests/v3/mqtt_mock_server_handler.c @@ -30,6 +30,8 @@ struct mqtt_mock_server_handler { size_t pubacks_received; size_t ping_received; size_t connacks_avail; + bool session_present; + bool reflect_publishes; bool auto_ack; /* last ID used when sending PUBLISH (QoS1+) to client */ @@ -77,12 +79,14 @@ static int s_mqtt_mock_server_handler_process_packet( switch (packet_type) { case AWS_MQTT_PACKET_CONNECT: { size_t connacks_available = 0; + bool session_present = false; aws_mutex_lock(&server->synced.lock); AWS_LOGF_DEBUG( MOCK_LOG_SUBJECT, "server, CONNECT received, %llu available connacks.", (long long unsigned)server->synced.connacks_avail); connacks_available = server->synced.connacks_avail > 0 ? server->synced.connacks_avail-- : 0; + session_present = server->synced.session_present; aws_mutex_unlock(&server->synced.lock); if (connacks_available) { @@ -90,7 +94,7 @@ static int s_mqtt_mock_server_handler_process_packet( aws_channel_acquire_message_from_pool(server->slot->channel, AWS_IO_MESSAGE_APPLICATION_DATA, 256); struct aws_mqtt_packet_connack conn_ack; - err |= aws_mqtt_packet_connack_init(&conn_ack, false, AWS_MQTT_CONNECT_ACCEPTED); + err |= aws_mqtt_packet_connack_init(&conn_ack, session_present, AWS_MQTT_CONNECT_ACCEPTED); err |= aws_mqtt_packet_connack_encode(&connack_msg->message_data, &conn_ack); if (aws_channel_slot_send_message(server->slot, connack_msg, AWS_CHANNEL_DIR_WRITE)) { err |= 1; @@ -185,6 +189,7 @@ static int s_mqtt_mock_server_handler_process_packet( aws_mutex_lock(&server->synced.lock); bool auto_ack = server->synced.auto_ack; + bool reflect_publishes = server->synced.reflect_publishes; aws_mutex_unlock(&server->synced.lock); uint8_t qos = (publish_packet.fixed_header.flags >> 1) & 0x3; @@ -197,6 +202,23 @@ static int s_mqtt_mock_server_handler_process_packet( err |= aws_mqtt_packet_ack_encode(&puback_msg->message_data, &puback); err |= aws_channel_slot_send_message(server->slot, puback_msg, AWS_CHANNEL_DIR_WRITE); } + + if (reflect_publishes) { + struct aws_io_message *publish_msg = + aws_channel_acquire_message_from_pool(server->slot->channel, AWS_IO_MESSAGE_APPLICATION_DATA, 1024); + struct aws_mqtt_packet_publish publish; + // reusing the packet identifier here is weird but reasonably safe, they're separate id spaces + err |= aws_mqtt_packet_publish_init( + &publish, + false, + qos, + false, + publish_packet.topic_name, + publish_packet.packet_identifier, + publish_packet.payload); + err |= aws_mqtt_packet_publish_encode(&publish_msg->message_data, &publish); + err |= aws_channel_slot_send_message(server->slot, publish_msg, AWS_CHANNEL_DIR_WRITE); + } break; } @@ -466,6 +488,22 @@ void destroy_mqtt_mock_server(struct aws_channel_handler *handler) { aws_mem_release(handler->alloc, server); } +void mqtt_mock_server_set_session_present(struct aws_channel_handler *handler, bool session_present) { + struct mqtt_mock_server_handler *server = handler->impl; + + aws_mutex_lock(&server->synced.lock); + server->synced.session_present = session_present; + aws_mutex_unlock(&server->synced.lock); +} + +void mqtt_mock_server_set_publish_reflection(struct aws_channel_handler *handler, bool reflect_publishes) { + struct mqtt_mock_server_handler *server = handler->impl; + + aws_mutex_lock(&server->synced.lock); + server->synced.reflect_publishes = reflect_publishes; + aws_mutex_unlock(&server->synced.lock); +} + void mqtt_mock_server_set_max_ping_resp(struct aws_channel_handler *handler, size_t max_ping) { struct mqtt_mock_server_handler *server = handler->impl; diff --git a/tests/v3/mqtt_mock_server_handler.h b/tests/v3/mqtt_mock_server_handler.h index 170201e6..8187ae11 100644 --- a/tests/v3/mqtt_mock_server_handler.h +++ b/tests/v3/mqtt_mock_server_handler.h @@ -68,10 +68,21 @@ int mqtt_mock_server_send_publish_by_id( enum aws_mqtt_qos qos, bool retain); +/** + * Sets whether or not connacks return session present + */ +void mqtt_mock_server_set_session_present(struct aws_channel_handler *handler, bool session_present); + +/** + * Sets whether or not connacks return session present + */ +void mqtt_mock_server_set_publish_reflection(struct aws_channel_handler *handler, bool reflect_publishes); + /** * Set max number of PINGRESP that mock server will send back to client */ void mqtt_mock_server_set_max_ping_resp(struct aws_channel_handler *handler, size_t max_ping); + /** * Set max number of CONACK that mock server will send back to client */ diff --git a/tests/v5/mqtt5_testing_utils.c b/tests/v5/mqtt5_testing_utils.c index 4b682d83..9d1163c9 100644 --- a/tests/v5/mqtt5_testing_utils.c +++ b/tests/v5/mqtt5_testing_utils.c @@ -1765,4 +1765,4 @@ int aws_mqtt5_mock_server_send_packet( } return AWS_OP_SUCCESS; -} \ No newline at end of file +}