summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorrobot-contrib <[email protected]>2025-05-14 16:50:13 +0300
committerrobot-contrib <[email protected]>2025-05-14 17:05:53 +0300
commit7b6ce38ca5dde1bd2ab16ffa714a2a3d8c4ebe7f (patch)
tree728bb5e8eb7e4d7de275d53687a119354e1e55e6
parentb78b05b5986a1525f0dc69dcc0887cd7e855a31c (diff)
Update contrib/restricted/aws/aws-c-mqtt to 0.10.4
commit_hash:27ca94d1c91767f620f86a6e9a6af618b0771fdf
-rw-r--r--contrib/restricted/aws/aws-c-mqtt/.yandex_meta/devtools.copyrights.report8
-rw-r--r--contrib/restricted/aws/aws-c-mqtt/.yandex_meta/devtools.licenses.report8
-rw-r--r--contrib/restricted/aws/aws-c-mqtt/.yandex_meta/override.nix4
-rw-r--r--contrib/restricted/aws/aws-c-mqtt/README.md2
-rw-r--r--contrib/restricted/aws/aws-c-mqtt/include/aws/mqtt/client.h106
-rw-r--r--contrib/restricted/aws/aws-c-mqtt/include/aws/mqtt/mqtt.h17
-rw-r--r--contrib/restricted/aws/aws-c-mqtt/include/aws/mqtt/private/client_impl.h75
-rw-r--r--contrib/restricted/aws/aws-c-mqtt/include/aws/mqtt/private/client_impl_shared.h123
-rw-r--r--contrib/restricted/aws/aws-c-mqtt/include/aws/mqtt/private/fixed_header.h2
-rw-r--r--contrib/restricted/aws/aws-c-mqtt/include/aws/mqtt/private/mqtt311_decoder.h135
-rw-r--r--contrib/restricted/aws/aws-c-mqtt/include/aws/mqtt/private/mqtt_client_test_helper.h2
-rw-r--r--contrib/restricted/aws/aws-c-mqtt/include/aws/mqtt/private/mqtt_subscription_set.h227
-rw-r--r--contrib/restricted/aws/aws-c-mqtt/include/aws/mqtt/private/packets.h3
-rw-r--r--contrib/restricted/aws/aws-c-mqtt/include/aws/mqtt/private/v5/mqtt5_client_impl.h71
-rw-r--r--contrib/restricted/aws/aws-c-mqtt/include/aws/mqtt/private/v5/mqtt5_options_storage.h23
-rw-r--r--contrib/restricted/aws/aws-c-mqtt/include/aws/mqtt/private/v5/mqtt5_to_mqtt3_adapter_impl.h341
-rw-r--r--contrib/restricted/aws/aws-c-mqtt/include/aws/mqtt/private/v5/mqtt5_utils.h31
-rw-r--r--contrib/restricted/aws/aws-c-mqtt/include/aws/mqtt/v5/mqtt5_client.h60
-rw-r--r--contrib/restricted/aws/aws-c-mqtt/include/aws/mqtt/v5/mqtt5_listener.h3
-rw-r--r--contrib/restricted/aws/aws-c-mqtt/include/aws/mqtt/v5/mqtt5_packet_storage.h11
-rw-r--r--contrib/restricted/aws/aws-c-mqtt/include/aws/mqtt/v5/mqtt5_types.h11
-rw-r--r--contrib/restricted/aws/aws-c-mqtt/source/client.c1100
-rw-r--r--contrib/restricted/aws/aws-c-mqtt/source/client_channel_handler.c435
-rw-r--r--contrib/restricted/aws/aws-c-mqtt/source/client_impl_shared.c220
-rw-r--r--contrib/restricted/aws/aws-c-mqtt/source/fixed_header.c5
-rw-r--r--contrib/restricted/aws/aws-c-mqtt/source/mqtt.c69
-rw-r--r--contrib/restricted/aws/aws-c-mqtt/source/mqtt311_decoder.c211
-rw-r--r--contrib/restricted/aws/aws-c-mqtt/source/mqtt_subscription_set.c431
-rw-r--r--contrib/restricted/aws/aws-c-mqtt/source/packets.c4
-rw-r--r--contrib/restricted/aws/aws-c-mqtt/source/topic_tree.c12
-rw-r--r--contrib/restricted/aws/aws-c-mqtt/source/v5/mqtt5_client.c386
-rw-r--r--contrib/restricted/aws/aws-c-mqtt/source/v5/mqtt5_encoder.c18
-rw-r--r--contrib/restricted/aws/aws-c-mqtt/source/v5/mqtt5_options_storage.c243
-rw-r--r--contrib/restricted/aws/aws-c-mqtt/source/v5/mqtt5_to_mqtt3_adapter.c3039
-rw-r--r--contrib/restricted/aws/aws-c-mqtt/source/v5/mqtt5_topic_alias.c78
-rw-r--r--contrib/restricted/aws/aws-c-mqtt/source/v5/mqtt5_utils.c126
-rw-r--r--contrib/restricted/aws/aws-c-mqtt/ya.make9
37 files changed, 6392 insertions, 1257 deletions
diff --git a/contrib/restricted/aws/aws-c-mqtt/.yandex_meta/devtools.copyrights.report b/contrib/restricted/aws/aws-c-mqtt/.yandex_meta/devtools.copyrights.report
index e3ea41760ee..fb8c16b3fff 100644
--- a/contrib/restricted/aws/aws-c-mqtt/.yandex_meta/devtools.copyrights.report
+++ b/contrib/restricted/aws/aws-c-mqtt/.yandex_meta/devtools.copyrights.report
@@ -47,8 +47,11 @@ BELONGS ya.make
include/aws/mqtt/exports.h [4:4]
include/aws/mqtt/mqtt.h [5:5]
include/aws/mqtt/private/client_impl.h [5:5]
+ include/aws/mqtt/private/client_impl_shared.h [5:5]
include/aws/mqtt/private/fixed_header.h [5:5]
+ include/aws/mqtt/private/mqtt311_decoder.h [5:5]
include/aws/mqtt/private/mqtt_client_test_helper.h [4:4]
+ include/aws/mqtt/private/mqtt_subscription_set.h [2:2]
include/aws/mqtt/private/packets.h [5:5]
include/aws/mqtt/private/shared_constants.h [2:2]
include/aws/mqtt/private/topic_tree.h [5:5]
@@ -57,6 +60,7 @@ BELONGS ya.make
include/aws/mqtt/private/v5/mqtt5_decoder.h [2:2]
include/aws/mqtt/private/v5/mqtt5_encoder.h [2:2]
include/aws/mqtt/private/v5/mqtt5_options_storage.h [5:5]
+ include/aws/mqtt/private/v5/mqtt5_to_mqtt3_adapter_impl.h [2:2]
include/aws/mqtt/private/v5/mqtt5_topic_alias.h [2:2]
include/aws/mqtt/private/v5/mqtt5_utils.h [5:5]
include/aws/mqtt/private/v5/rate_limiters.h [2:2]
@@ -66,8 +70,11 @@ BELONGS ya.make
include/aws/mqtt/v5/mqtt5_types.h [5:5]
source/client.c [2:2]
source/client_channel_handler.c [2:2]
+ source/client_impl_shared.c [2:2]
source/fixed_header.c [2:2]
source/mqtt.c [2:2]
+ source/mqtt311_decoder.c [2:2]
+ source/mqtt_subscription_set.c [2:2]
source/packets.c [2:2]
source/shared_constants.c [2:2]
source/topic_tree.c [2:2]
@@ -77,6 +84,7 @@ BELONGS ya.make
source/v5/mqtt5_encoder.c [2:2]
source/v5/mqtt5_listener.c [2:2]
source/v5/mqtt5_options_storage.c [2:2]
+ source/v5/mqtt5_to_mqtt3_adapter.c [2:2]
source/v5/mqtt5_topic_alias.c [2:2]
source/v5/mqtt5_types.c [2:2]
source/v5/mqtt5_utils.c [2:2]
diff --git a/contrib/restricted/aws/aws-c-mqtt/.yandex_meta/devtools.licenses.report b/contrib/restricted/aws/aws-c-mqtt/.yandex_meta/devtools.licenses.report
index 5e96c1ab427..6fc0570ab0d 100644
--- a/contrib/restricted/aws/aws-c-mqtt/.yandex_meta/devtools.licenses.report
+++ b/contrib/restricted/aws/aws-c-mqtt/.yandex_meta/devtools.licenses.report
@@ -96,8 +96,11 @@ BELONGS ya.make
include/aws/mqtt/exports.h [5:5]
include/aws/mqtt/mqtt.h [6:6]
include/aws/mqtt/private/client_impl.h [6:6]
+ include/aws/mqtt/private/client_impl_shared.h [6:6]
include/aws/mqtt/private/fixed_header.h [6:6]
+ include/aws/mqtt/private/mqtt311_decoder.h [6:6]
include/aws/mqtt/private/mqtt_client_test_helper.h [5:5]
+ include/aws/mqtt/private/mqtt_subscription_set.h [3:3]
include/aws/mqtt/private/packets.h [6:6]
include/aws/mqtt/private/shared_constants.h [3:3]
include/aws/mqtt/private/topic_tree.h [6:6]
@@ -106,6 +109,7 @@ BELONGS ya.make
include/aws/mqtt/private/v5/mqtt5_decoder.h [3:3]
include/aws/mqtt/private/v5/mqtt5_encoder.h [3:3]
include/aws/mqtt/private/v5/mqtt5_options_storage.h [6:6]
+ include/aws/mqtt/private/v5/mqtt5_to_mqtt3_adapter_impl.h [3:3]
include/aws/mqtt/private/v5/mqtt5_topic_alias.h [3:3]
include/aws/mqtt/private/v5/mqtt5_utils.h [6:6]
include/aws/mqtt/private/v5/rate_limiters.h [3:3]
@@ -115,8 +119,11 @@ BELONGS ya.make
include/aws/mqtt/v5/mqtt5_types.h [6:6]
source/client.c [3:3]
source/client_channel_handler.c [3:3]
+ source/client_impl_shared.c [3:3]
source/fixed_header.c [3:3]
source/mqtt.c [3:3]
+ source/mqtt311_decoder.c [3:3]
+ source/mqtt_subscription_set.c [3:3]
source/packets.c [3:3]
source/shared_constants.c [3:3]
source/topic_tree.c [3:3]
@@ -126,6 +133,7 @@ BELONGS ya.make
source/v5/mqtt5_encoder.c [3:3]
source/v5/mqtt5_listener.c [3:3]
source/v5/mqtt5_options_storage.c [3:3]
+ source/v5/mqtt5_to_mqtt3_adapter.c [3:3]
source/v5/mqtt5_topic_alias.c [3:3]
source/v5/mqtt5_types.c [3:3]
source/v5/mqtt5_utils.c [3:3]
diff --git a/contrib/restricted/aws/aws-c-mqtt/.yandex_meta/override.nix b/contrib/restricted/aws/aws-c-mqtt/.yandex_meta/override.nix
index 1361221b408..f02bd667fdb 100644
--- a/contrib/restricted/aws/aws-c-mqtt/.yandex_meta/override.nix
+++ b/contrib/restricted/aws/aws-c-mqtt/.yandex_meta/override.nix
@@ -1,10 +1,10 @@
pkgs: attrs: with pkgs; with attrs; rec {
- version = "0.8.8";
+ version = "0.10.4";
src = fetchFromGitHub {
owner = "awslabs";
repo = "aws-c-mqtt";
rev = "v${version}";
- hash = "sha256-bt5Qjw+CqgTfi/Ibhc4AwmJxr22Q6m3ygpmeMhvQTT0=";
+ hash = "sha256-i+ssZzHC8MPfyOaRqvjq0z7w772BJqIA6BwntW1fRek=";
};
}
diff --git a/contrib/restricted/aws/aws-c-mqtt/README.md b/contrib/restricted/aws/aws-c-mqtt/README.md
index 070aab53b5e..2f99277e399 100644
--- a/contrib/restricted/aws/aws-c-mqtt/README.md
+++ b/contrib/restricted/aws/aws-c-mqtt/README.md
@@ -1,6 +1,6 @@
## AWS C MQTT
-C99 implementation of the MQTT 3.1.1 specification.
+C99 implementation of the MQTT 3.1.1 and MQTT 5 specifications.
## License
diff --git a/contrib/restricted/aws/aws-c-mqtt/include/aws/mqtt/client.h b/contrib/restricted/aws/aws-c-mqtt/include/aws/mqtt/client.h
index f12a5c19b5a..8d85bfe89f3 100644
--- a/contrib/restricted/aws/aws-c-mqtt/include/aws/mqtt/client.h
+++ b/contrib/restricted/aws/aws-c-mqtt/include/aws/mqtt/client.h
@@ -17,11 +17,14 @@
#include <aws/mqtt/mqtt.h>
+AWS_PUSH_SANE_WARNING_LEVEL
+
/* forward declares */
struct aws_client_bootstrap;
struct aws_http_header;
struct aws_http_message;
struct aws_http_proxy_options;
+struct aws_mqtt5_client;
struct aws_socket_options;
struct aws_tls_connection_options;
@@ -63,6 +66,27 @@ typedef void(aws_mqtt_client_on_connection_complete_fn)(
bool session_present,
void *userdata);
+/* Called when a connection attempt succeed (with a successful CONNACK)
+ *
+ * The callback is derived from aws_mqtt_client_on_connection_complete_fn.
+ * It gets triggered when connection succeed (with a successful CONNACK)
+ */
+typedef void(aws_mqtt_client_on_connection_success_fn)(
+ struct aws_mqtt_client_connection *connection,
+ enum aws_mqtt_connect_return_code return_code,
+ bool session_present,
+ void *userdata);
+
+/* Called if the connection attempt failed.
+ *
+ * The callback is derived from aws_mqtt_client_on_connection_complete_fn.
+ * It gets triggered when connection failed.
+ */
+typedef void(aws_mqtt_client_on_connection_failure_fn)(
+ struct aws_mqtt_client_connection *connection,
+ int error_code,
+ void *userdata);
+
/* Called if the connection to the server is lost. */
typedef void(aws_mqtt_client_on_connection_interrupted_fn)(
struct aws_mqtt_client_connection *connection,
@@ -138,6 +162,11 @@ typedef void(aws_mqtt_client_publish_received_fn)(
typedef void(aws_mqtt_client_on_disconnect_fn)(struct aws_mqtt_client_connection *connection, void *userdata);
/**
+ * Signature of callback invoked on a connection destruction.
+ */
+typedef void(aws_mqtt_client_on_connection_termination_fn)(void *userdata);
+
+/**
* Function to invoke when the websocket handshake request transformation completes.
* This function MUST be invoked or the application will soft-lock.
*
@@ -225,7 +254,7 @@ struct aws_mqtt_topic_subscription {
*/
struct aws_mqtt_connection_options {
struct aws_byte_cursor host_name;
- uint16_t port;
+ uint32_t port;
struct aws_socket_options *socket_options;
struct aws_tls_connection_options *tls_options;
struct aws_byte_cursor client_id;
@@ -308,6 +337,16 @@ AWS_MQTT_API
struct aws_mqtt_client_connection *aws_mqtt_client_connection_new(struct aws_mqtt_client *client);
/**
+ * Creates a new MQTT311 connection object that uses an MQTT5 client under the hood
+ *
+ * \param[in] client The mqtt5 client to create the connection from
+ *
+ * \returns a new mqtt (311) connection on success, NULL otherwise
+ */
+AWS_MQTT_API
+struct aws_mqtt_client_connection *aws_mqtt_client_connection_new_from_mqtt5_client(struct aws_mqtt5_client *client);
+
+/**
* Increments the ref count to an mqtt client connection, allowing the caller to take a reference to it
*
* \param[in] connection The connection object
@@ -387,6 +426,14 @@ int aws_mqtt_client_connection_set_http_proxy_options(
struct aws_http_proxy_options *proxy_options);
/**
+ * Set host resolution ooptions for the connection.
+ */
+AWS_MQTT_API
+int aws_mqtt_client_connection_set_host_resolution_options(
+ struct aws_mqtt_client_connection *connection,
+ const struct aws_host_resolution_config *host_resolution_config);
+
+/**
* Sets the minimum and maximum reconnect timeouts.
*
* The time between reconnect attempts will start at min and multiply by 2 until max is reached.
@@ -402,6 +449,23 @@ int aws_mqtt_client_connection_set_reconnect_timeout(
uint64_t max_timeout);
/**
+ * Sets the callbacks to call when a connection succeeds or fails
+ *
+ * \param[in] connection The connection object
+ * \param[in] on_connection_success The function to call when a connection is successful or gets resumed
+ * \param[in] on_connection_success_ud Userdata for on_connection_success
+ * \param[in] on_connection_failure The function to call when a connection fails
+ * \param[in] on_connection_failure_ud Userdata for on_connection_failure
+ */
+AWS_MQTT_API
+int aws_mqtt_client_connection_set_connection_result_handlers(
+ struct aws_mqtt_client_connection *connection,
+ aws_mqtt_client_on_connection_success_fn *on_connection_success,
+ void *on_connection_success_ud,
+ aws_mqtt_client_on_connection_failure_fn *on_connection_failure,
+ void *on_connection_failure_ud);
+
+/**
* Sets the callbacks to call when a connection is interrupted and resumed.
*
* \param[in] connection The connection object
@@ -448,6 +512,19 @@ int aws_mqtt_client_connection_set_on_any_publish_handler(
void *on_any_publish_ud);
/**
+ * Sets the callback to call on a connection destruction.
+ *
+ * \param[in] connection The connection object.
+ * \param[in] on_termination The function to call when a connection is destroyed.
+ * \param[in] on_termination_ud Userdata for on_termination.
+ */
+AWS_MQTT_API
+int aws_mqtt_client_connection_set_connection_termination_handler(
+ struct aws_mqtt_client_connection *connection,
+ aws_mqtt_client_on_connection_termination_fn *on_termination,
+ void *on_termination_ud);
+
+/**
* Opens the actual connection defined by aws_mqtt_client_connection_new.
* Once the connection is opened, on_connack will be called. Only called when connection is disconnected.
*
@@ -547,32 +624,6 @@ uint16_t aws_mqtt_client_connection_subscribe(
void *on_suback_ud);
/**
- * Subscribe to a single topic filter WITHOUT sending a SUBSCRIBE packet.
- * This is useful if you expect the broker to send PUBLISHES without first subscribing.
- * on_publish will be called when a PUBLISH matching topic_filter is received.
- *
- * \param[in] connection The connection to subscribe on
- * \param[in] topic_filter The topic filter to subscribe on. This resource must persist until on_suback.
- * \param[in] on_publish (nullable) Called when a PUBLISH packet matching topic_filter is received
- * \param[in] on_publish_ud (nullable) Passed to on_publish
- * \param[in] on_ud_cleanup (nullable) Called when a subscription is removed, on_publish_ud is passed.
- * \param[in] on_suback (nullable) Called when a SUBACK has been received from the server and the subscription is
- * complete
- * \param[in] on_suback_ud (nullable) Passed to on_suback
- *
- * \returns The "packet id" of the operation if successfully initiated, otherwise 0.
- */
-AWS_MQTT_API
-uint16_t aws_mqtt_client_connection_subscribe_local(
- struct aws_mqtt_client_connection *connection,
- const struct aws_byte_cursor *topic_filter,
- aws_mqtt_client_publish_received_fn *on_publish,
- void *on_publish_ud,
- aws_mqtt_userdata_cleanup_fn *on_ud_cleanup,
- aws_mqtt_suback_fn *on_suback,
- void *on_suback_ud);
-
-/**
* Resubscribe to all topics currently subscribed to. This is to help when resuming a connection with a clean session.
*
* \param[in] connection The connection to subscribe on
@@ -643,5 +694,6 @@ int aws_mqtt_client_connection_get_stats(
struct aws_mqtt_connection_operation_statistics *stats);
AWS_EXTERN_C_END
+AWS_POP_SANE_WARNING_LEVEL
#endif /* AWS_MQTT_CLIENT_H */
diff --git a/contrib/restricted/aws/aws-c-mqtt/include/aws/mqtt/mqtt.h b/contrib/restricted/aws/aws-c-mqtt/include/aws/mqtt/mqtt.h
index 22a63ce2259..38510c8c80e 100644
--- a/contrib/restricted/aws/aws-c-mqtt/include/aws/mqtt/mqtt.h
+++ b/contrib/restricted/aws/aws-c-mqtt/include/aws/mqtt/mqtt.h
@@ -11,6 +11,8 @@
#include <aws/mqtt/exports.h>
+AWS_PUSH_SANE_WARNING_LEVEL
+
#define AWS_C_MQTT_PACKAGE_ID 5
/* Quality of Service associated with a publish action or subscription [MQTT-4.3]. */
@@ -76,6 +78,10 @@ enum aws_mqtt_error {
AWS_ERROR_MQTT5_INVALID_INBOUND_TOPIC_ALIAS,
AWS_ERROR_MQTT5_INVALID_OUTBOUND_TOPIC_ALIAS,
AWS_ERROR_MQTT5_INVALID_UTF8_STRING,
+ AWS_ERROR_MQTT_CONNECTION_RESET_FOR_ADAPTER_CONNECT,
+ AWS_ERROR_MQTT_CONNECTION_RESUBSCRIBE_NO_TOPICS,
+ AWS_ERROR_MQTT_CONNECTION_SUBSCRIBE_FAILURE,
+ AWS_ERROR_MQTT_ACK_REASON_CODE_FAILURE,
AWS_ERROR_END_MQTT_RANGE = AWS_ERROR_ENUM_END_RANGE(AWS_C_MQTT_PACKAGE_ID),
};
@@ -87,6 +93,7 @@ enum aws_mqtt_log_subject {
AWS_LS_MQTT5_GENERAL,
AWS_LS_MQTT5_CLIENT,
AWS_LS_MQTT5_CANARY,
+ AWS_LS_MQTT5_TO_MQTT3_ADAPTER,
};
/** Function called on cleanup of a userdata. */
@@ -96,10 +103,19 @@ AWS_EXTERN_C_BEGIN
AWS_MQTT_API
bool aws_mqtt_is_valid_topic(const struct aws_byte_cursor *topic);
+
AWS_MQTT_API
bool aws_mqtt_is_valid_topic_filter(const struct aws_byte_cursor *topic_filter);
/**
+ * Validate utf-8 string under mqtt specs
+ *
+ * @param text
+ * @return AWS_OP_SUCCESS if the text is validate, otherwise AWS_OP_ERR
+ */
+AWS_MQTT_API int aws_mqtt_validate_utf8_text(struct aws_byte_cursor text);
+
+/**
* Initializes internal datastructures used by aws-c-mqtt.
* Must be called before using any functionality in aws-c-mqtt.
*/
@@ -116,5 +132,6 @@ AWS_MQTT_API
void aws_mqtt_fatal_assert_library_initialized(void);
AWS_EXTERN_C_END
+AWS_POP_SANE_WARNING_LEVEL
#endif /* AWS_MQTT_MQTT_H */
diff --git a/contrib/restricted/aws/aws-c-mqtt/include/aws/mqtt/private/client_impl.h b/contrib/restricted/aws/aws-c-mqtt/include/aws/mqtt/private/client_impl.h
index 6bdfe749c91..1d0dd67a0c2 100644
--- a/contrib/restricted/aws/aws-c-mqtt/include/aws/mqtt/private/client_impl.h
+++ b/contrib/restricted/aws/aws-c-mqtt/include/aws/mqtt/private/client_impl.h
@@ -8,7 +8,9 @@
#include <aws/mqtt/client.h>
+#include <aws/mqtt/private/client_impl_shared.h>
#include <aws/mqtt/private/fixed_header.h>
+#include <aws/mqtt/private/mqtt311_decoder.h>
#include <aws/mqtt/private/topic_tree.h>
#include <aws/common/hash_table.h>
@@ -21,16 +23,18 @@
#include <aws/io/socket.h>
#include <aws/io/tls_channel_handler.h>
+struct aws_mqtt_client_connection_311_impl;
+
#define MQTT_CLIENT_CALL_CALLBACK(client_ptr, callback) \
do { \
if ((client_ptr)->callback) { \
- (client_ptr)->callback((client_ptr), (client_ptr)->callback##_ud); \
+ (client_ptr)->callback((&client_ptr->base), (client_ptr)->callback##_ud); \
} \
} while (false)
#define MQTT_CLIENT_CALL_CALLBACK_ARGS(client_ptr, callback, ...) \
do { \
if ((client_ptr)->callback) { \
- (client_ptr)->callback((client_ptr), __VA_ARGS__, (client_ptr)->callback##_ud); \
+ (client_ptr)->callback((&client_ptr->base), __VA_ARGS__, (client_ptr)->callback##_ud); \
} \
} while (false)
@@ -101,7 +105,8 @@ typedef enum aws_mqtt_client_request_state(
/**
* Called when the operation statistics change.
*/
-typedef void(aws_mqtt_on_operation_statistics_fn)(struct aws_mqtt_client_connection *connection, void *userdata);
+typedef void(
+ aws_mqtt_on_operation_statistics_fn)(struct aws_mqtt_client_connection_311_impl *connection, void *userdata);
/* Flags that indicate the way in which way an operation is currently affecting the statistics of the connection */
enum aws_mqtt_operation_statistic_state_flags {
@@ -119,10 +124,15 @@ struct aws_mqtt_request {
struct aws_linked_list_node list_node;
struct aws_allocator *allocator;
- struct aws_mqtt_client_connection *connection;
+ struct aws_mqtt_client_connection_311_impl *connection;
struct aws_channel_task outgoing_task;
+ /*
+ * The request send time. Currently used to push off keepalive packet.
+ */
+ uint64_t request_send_timestamp;
+
/* How this operation is currently affecting the statistics of the connection */
enum aws_mqtt_operation_statistic_state_flags statistic_state_flags;
/* The encoded size of the packet - used for operation statistics tracking */
@@ -146,7 +156,7 @@ struct aws_mqtt_reconnect_task {
/* The lifetime of this struct is from subscribe -> suback */
struct subscribe_task_arg {
- struct aws_mqtt_client_connection *connection;
+ struct aws_mqtt_client_connection_311_impl *connection;
/* list of pointer of subscribe_task_topics */
struct aws_array_list topics;
@@ -166,19 +176,21 @@ struct subscribe_task_arg {
/* The lifetime of this struct is the same as the lifetime of the subscription */
struct subscribe_task_topic {
- struct aws_mqtt_client_connection *connection;
+ struct aws_mqtt_client_connection_311_impl *connection;
struct aws_mqtt_topic_subscription request;
struct aws_string *filter;
- bool is_local;
struct aws_ref_count ref_count;
};
-struct aws_mqtt_client_connection {
-
+struct aws_mqtt_client_connection_311_impl {
struct aws_allocator *allocator;
+
+ struct aws_mqtt_client_connection base;
+
struct aws_ref_count ref_count;
+
struct aws_mqtt_client *client;
/* Channel handler information */
@@ -187,16 +199,18 @@ struct aws_mqtt_client_connection {
/* The host information, changed by user when state is AWS_MQTT_CLIENT_STATE_DISCONNECTED */
struct aws_string *host_name;
- uint16_t port;
+ uint32_t port;
struct aws_tls_connection_options tls_options;
struct aws_socket_options socket_options;
struct aws_http_proxy_config *http_proxy_config;
struct aws_event_loop *loop;
+ struct aws_host_resolution_config host_resolution_config;
/* Connect parameters */
struct aws_byte_buf client_id;
bool clean_session;
uint16_t keep_alive_time_secs;
+ uint64_t keep_alive_time_ns;
uint64_t ping_timeout_ns;
uint64_t operation_timeout_ns;
struct aws_string *username;
@@ -222,6 +236,10 @@ struct aws_mqtt_client_connection {
/* User connection callbacks */
aws_mqtt_client_on_connection_complete_fn *on_connection_complete;
void *on_connection_complete_ud;
+ aws_mqtt_client_on_connection_success_fn *on_connection_success;
+ void *on_connection_success_ud;
+ aws_mqtt_client_on_connection_failure_fn *on_connection_failure;
+ void *on_connection_failure_ud;
aws_mqtt_client_on_connection_interrupted_fn *on_interrupted;
void *on_interrupted_ud;
aws_mqtt_client_on_connection_resumed_fn *on_resumed;
@@ -232,6 +250,8 @@ struct aws_mqtt_client_connection {
void *on_any_publish_ud;
aws_mqtt_client_on_disconnect_fn *on_disconnect;
void *on_disconnect_ud;
+ aws_mqtt_client_on_connection_termination_fn *on_termination;
+ void *on_termination_ud;
aws_mqtt_on_operation_statistics_fn *on_any_operation_statistics;
void *on_any_operation_statistics_ud;
@@ -249,8 +269,7 @@ struct aws_mqtt_client_connection {
/* Only the event-loop thread may touch this data */
struct {
- /* If an incomplete packet arrives, store the data here. */
- struct aws_byte_buf pending_packet;
+ struct aws_mqtt311_decoder decoder;
bool waiting_on_ping_response;
@@ -309,6 +328,14 @@ struct aws_mqtt_client_connection {
} websocket;
/**
+ * The time that the next ping task should execute at. Note that this does not mean that
+ * this IS when the ping task will execute, but rather that this is when the next ping
+ * SHOULD execute. There may be an already scheduled PING task that will elapse sooner
+ * than this time that has to be rescheduled.
+ */
+ uint64_t next_ping_time;
+
+ /**
* Statistics tracking operational state
*/
struct aws_mqtt_connection_operation_statistics_impl operation_statistics_impl;
@@ -318,15 +345,15 @@ struct aws_channel_handler_vtable *aws_mqtt_get_client_channel_vtable(void);
/* Helper for getting a message object for a packet */
struct aws_io_message *mqtt_get_message_for_packet(
- struct aws_mqtt_client_connection *connection,
+ struct aws_mqtt_client_connection_311_impl *connection,
struct aws_mqtt_fixed_header *header);
-void mqtt_connection_lock_synced_data(struct aws_mqtt_client_connection *connection);
-void mqtt_connection_unlock_synced_data(struct aws_mqtt_client_connection *connection);
+void mqtt_connection_lock_synced_data(struct aws_mqtt_client_connection_311_impl *connection);
+void mqtt_connection_unlock_synced_data(struct aws_mqtt_client_connection_311_impl *connection);
/* Note: needs to be called with lock held. */
void mqtt_connection_set_state(
- struct aws_mqtt_client_connection *connection,
+ struct aws_mqtt_client_connection_311_impl *connection,
enum aws_mqtt_client_connection_state state);
/**
@@ -336,7 +363,7 @@ void mqtt_connection_set_state(
* noRetry is true for the packets will never be retried or offline queued.
*/
AWS_MQTT_API uint16_t mqtt_create_request(
- struct aws_mqtt_client_connection *connection,
+ struct aws_mqtt_client_connection_311_impl *connection,
aws_mqtt_send_request_fn *send_request,
void *send_request_ud,
aws_mqtt_op_complete_fn *on_complete,
@@ -346,15 +373,15 @@ AWS_MQTT_API uint16_t mqtt_create_request(
/* Call when an ack packet comes back from the server. */
AWS_MQTT_API void mqtt_request_complete(
- struct aws_mqtt_client_connection *connection,
+ struct aws_mqtt_client_connection_311_impl *connection,
int error_code,
uint16_t packet_id);
/* Call to close the connection with an error code */
-AWS_MQTT_API void mqtt_disconnect_impl(struct aws_mqtt_client_connection *connection, int error_code);
+AWS_MQTT_API void mqtt_disconnect_impl(struct aws_mqtt_client_connection_311_impl *connection, int error_code);
/* Creates the task used to reestablish a broken connection */
-AWS_MQTT_API void aws_create_reconnect_task(struct aws_mqtt_client_connection *connection);
+AWS_MQTT_API void aws_create_reconnect_task(struct aws_mqtt_client_connection_311_impl *connection);
/**
* Sets the callback to call whenever the operation statistics change.
@@ -364,7 +391,7 @@ AWS_MQTT_API void aws_create_reconnect_task(struct aws_mqtt_client_connection *c
* \param[in] on_operation_statistics_ud Userdata for on_operation_statistics
*/
AWS_MQTT_API int aws_mqtt_client_connection_set_on_operation_statistics_handler(
- struct aws_mqtt_client_connection *connection,
+ struct aws_mqtt_client_connection_311_impl *connection,
aws_mqtt_on_operation_statistics_fn *on_operation_statistics,
void *on_operation_statistics_ud);
@@ -378,7 +405,7 @@ AWS_MQTT_API int aws_mqtt_client_connection_set_on_operation_statistics_handler(
* \returns AWS_OP_SUCCESS if the connection is open and the PINGREQ is sent or queued to send,
* otherwise AWS_OP_ERR and aws_last_error() is set.
*/
-int aws_mqtt_client_connection_ping(struct aws_mqtt_client_connection *connection);
+int aws_mqtt_client_connection_ping(struct aws_mqtt_client_connection_311_impl *connection);
/**
* Changes the operation statistics for the passed-in aws_mqtt_request. Used for tracking
@@ -392,8 +419,10 @@ int aws_mqtt_client_connection_ping(struct aws_mqtt_client_connection *connectio
* @param new_state_flags The new state to use
*/
void aws_mqtt_connection_statistics_change_operation_statistic_state(
- struct aws_mqtt_client_connection *connection,
+ struct aws_mqtt_client_connection_311_impl *connection,
struct aws_mqtt_request *request,
enum aws_mqtt_operation_statistic_state_flags new_state_flags);
+AWS_MQTT_API const struct aws_mqtt_client_connection_packet_handlers *aws_mqtt311_get_default_packet_handlers(void);
+
#endif /* AWS_MQTT_PRIVATE_CLIENT_IMPL_H */
diff --git a/contrib/restricted/aws/aws-c-mqtt/include/aws/mqtt/private/client_impl_shared.h b/contrib/restricted/aws/aws-c-mqtt/include/aws/mqtt/private/client_impl_shared.h
new file mode 100644
index 00000000000..d244cfe7c66
--- /dev/null
+++ b/contrib/restricted/aws/aws-c-mqtt/include/aws/mqtt/private/client_impl_shared.h
@@ -0,0 +1,123 @@
+#ifndef AWS_MQTT_PRIVATE_CLIENT_IMPL_SHARED_H
+#define AWS_MQTT_PRIVATE_CLIENT_IMPL_SHARED_H
+
+/**
+ * Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved.
+ * SPDX-License-Identifier: Apache-2.0.
+ */
+
+#include <aws/mqtt/client.h>
+
+struct aws_mqtt_client_connection;
+
+struct aws_mqtt_client_connection_vtable {
+
+ struct aws_mqtt_client_connection *(*acquire_fn)(void *impl);
+
+ void (*release_fn)(void *impl);
+
+ int (*set_will_fn)(
+ void *impl,
+ const struct aws_byte_cursor *topic,
+ enum aws_mqtt_qos qos,
+ bool retain,
+ const struct aws_byte_cursor *payload);
+
+ int (*set_login_fn)(void *impl, const struct aws_byte_cursor *username, const struct aws_byte_cursor *password);
+
+ int (*use_websockets_fn)(
+ void *impl,
+ aws_mqtt_transform_websocket_handshake_fn *transformer,
+ void *transformer_ud,
+ aws_mqtt_validate_websocket_handshake_fn *validator,
+ void *validator_ud);
+
+ int (*set_http_proxy_options_fn)(void *impl, struct aws_http_proxy_options *proxy_options);
+
+ int (*set_host_resolution_options_fn)(void *impl, const struct aws_host_resolution_config *host_resolution_config);
+
+ int (*set_reconnect_timeout_fn)(void *impl, uint64_t min_timeout, uint64_t max_timeout);
+
+ int (*set_connection_interruption_handlers_fn)(
+ void *impl,
+ aws_mqtt_client_on_connection_interrupted_fn *on_interrupted,
+ void *on_interrupted_ud,
+ aws_mqtt_client_on_connection_resumed_fn *on_resumed,
+ void *on_resumed_ud);
+
+ int (*set_connection_result_handlers)(
+ void *impl,
+ aws_mqtt_client_on_connection_success_fn *on_connection_success,
+ void *on_connection_success_ud,
+ aws_mqtt_client_on_connection_failure_fn *on_connection_failure,
+ void *on_connection_failure_ud);
+
+ int (*set_connection_closed_handler_fn)(
+ void *impl,
+ aws_mqtt_client_on_connection_closed_fn *on_closed,
+ void *on_closed_ud);
+
+ int (*set_on_any_publish_handler_fn)(
+ void *impl,
+ aws_mqtt_client_publish_received_fn *on_any_publish,
+ void *on_any_publish_ud);
+
+ int (*set_connection_termination_handler_fn)(
+ void *impl,
+ aws_mqtt_client_on_connection_termination_fn *on_termination,
+ void *on_termination_ud);
+
+ int (*connect_fn)(void *impl, const struct aws_mqtt_connection_options *connection_options);
+
+ int (*reconnect_fn)(void *impl, aws_mqtt_client_on_connection_complete_fn *on_connection_complete, void *userdata);
+
+ int (*disconnect_fn)(void *impl, aws_mqtt_client_on_disconnect_fn *on_disconnect, void *userdata);
+
+ uint16_t (*subscribe_multiple_fn)(
+ void *impl,
+ const struct aws_array_list *topic_filters,
+ aws_mqtt_suback_multi_fn *on_suback,
+ void *on_suback_ud);
+
+ uint16_t (*subscribe_fn)(
+ void *impl,
+ const struct aws_byte_cursor *topic_filter,
+ enum aws_mqtt_qos qos,
+ aws_mqtt_client_publish_received_fn *on_publish,
+ void *on_publish_ud,
+ aws_mqtt_userdata_cleanup_fn *on_ud_cleanup,
+ aws_mqtt_suback_fn *on_suback,
+ void *on_suback_ud);
+
+ uint16_t (*resubscribe_existing_topics_fn)(void *impl, aws_mqtt_suback_multi_fn *on_suback, void *on_suback_ud);
+
+ uint16_t (*unsubscribe_fn)(
+ void *impl,
+ const struct aws_byte_cursor *topic_filter,
+ aws_mqtt_op_complete_fn *on_unsuback,
+ void *on_unsuback_ud);
+
+ uint16_t (*publish_fn)(
+ void *impl,
+ const struct aws_byte_cursor *topic,
+ enum aws_mqtt_qos qos,
+ bool retain,
+ const struct aws_byte_cursor *payload,
+ aws_mqtt_op_complete_fn *on_complete,
+ void *userdata);
+
+ int (*get_stats_fn)(void *impl, struct aws_mqtt_connection_operation_statistics *stats);
+};
+
+struct aws_mqtt_client_connection {
+ struct aws_mqtt_client_connection_vtable *vtable;
+ void *impl;
+};
+
+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);
+
+AWS_MQTT_API bool aws_mqtt_byte_cursor_hash_equality(const void *a, const void *b);
+
+#endif /* AWS_MQTT_PRIVATE_CLIENT_IMPL_SHARED_H */
diff --git a/contrib/restricted/aws/aws-c-mqtt/include/aws/mqtt/private/fixed_header.h b/contrib/restricted/aws/aws-c-mqtt/include/aws/mqtt/private/fixed_header.h
index 4944c86fe95..98e2758a98d 100644
--- a/contrib/restricted/aws/aws-c-mqtt/include/aws/mqtt/private/fixed_header.h
+++ b/contrib/restricted/aws/aws-c-mqtt/include/aws/mqtt/private/fixed_header.h
@@ -59,4 +59,6 @@ AWS_MQTT_API int aws_mqtt_fixed_header_encode(struct aws_byte_buf *buf, const st
*/
AWS_MQTT_API int aws_mqtt_fixed_header_decode(struct aws_byte_cursor *cur, struct aws_mqtt_fixed_header *header);
+AWS_MQTT_API int aws_mqtt311_decode_remaining_length(struct aws_byte_cursor *cur, size_t *remaining_length_out);
+
#endif /* AWS_MQTT_PRIVATE_FIXED_HEADER_H */
diff --git a/contrib/restricted/aws/aws-c-mqtt/include/aws/mqtt/private/mqtt311_decoder.h b/contrib/restricted/aws/aws-c-mqtt/include/aws/mqtt/private/mqtt311_decoder.h
new file mode 100644
index 00000000000..4147168d901
--- /dev/null
+++ b/contrib/restricted/aws/aws-c-mqtt/include/aws/mqtt/private/mqtt311_decoder.h
@@ -0,0 +1,135 @@
+#ifndef AWS_MQTT_PRIVATE_MQTT311_DECODER_H
+#define AWS_MQTT_PRIVATE_MQTT311_DECODER_H
+
+/**
+ * Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved.
+ * SPDX-License-Identifier: Apache-2.0.
+ */
+
+#include <aws/mqtt/mqtt.h>
+
+#include <aws/common/byte_buf.h>
+
+/*
+ * Per-packet-type callback signature. message_cursor contains the entire packet's data.
+ */
+typedef int(packet_handler_fn)(struct aws_byte_cursor message_cursor, void *user_data);
+
+/*
+ * Wrapper for a set of packet handlers for each possible MQTT packet type. Some values are invalid in 311 (15), and
+ * some values are only valid from the perspective of the server or client.
+ */
+struct aws_mqtt_client_connection_packet_handlers {
+ packet_handler_fn *handlers_by_packet_type[16];
+};
+
+/*
+ * Internal state of the 311 decoder/framing logic.
+ *
+ * When a packet is fragmented across multiple io buffers, state moves circularly:
+ * first byte -> remaining length -> body -> first byte etc...
+ *
+ * When a packet is completely contained inside a single io buffer, the entire packet is processed within
+ * the READ_FIRST_BYTE state.
+ */
+enum aws_mqtt_311_decoder_state_type {
+
+ /*
+ * The decoder is expecting the first byte of the fixed header of an MQTT control packet
+ */
+ AWS_MDST_READ_FIRST_BYTE,
+
+ /*
+ * The decoder is expecting the vli-encoded total remaining length field of the fixed header on an MQTT control
+ * packet.
+ */
+ AWS_MDST_READ_REMAINING_LENGTH,
+
+ /*
+ * The decoder is expecting the "rest" of the MQTT packet's data based on the remaining length value that has
+ * already been read.
+ */
+ AWS_MDST_READ_BODY,
+
+ /*
+ * Terminal state for when a protocol error has been encountered by the decoder. The only way to leave this
+ * state is to reset the decoder via the aws_mqtt311_decoder_reset_for_new_connection() API.
+ */
+ AWS_MDST_PROTOCOL_ERROR,
+};
+
+/*
+ * Configuration options for the decoder. When used by the actual implementation, handler_user_data is the
+ * connection object and the packet handlers are channel functions that hook into reactionary behavior and user
+ * callbacks.
+ */
+struct aws_mqtt311_decoder_options {
+ const struct aws_mqtt_client_connection_packet_handlers *packet_handlers;
+ void *handler_user_data;
+};
+
+/*
+ * Simple MQTT311 decoder structure. Actual decoding is deferred to per-packet functions that expect the whole
+ * packet in a buffer. The primary function of this sub-system is correctly framing a stream of bytes into the
+ * constituent packets.
+ */
+struct aws_mqtt311_decoder {
+ struct aws_mqtt311_decoder_options config;
+
+ enum aws_mqtt_311_decoder_state_type state;
+
+ /*
+ * If zero, not valid. If non-zero, represents the number of bytes that need to be read to finish the packet.
+ * This includes the total encoding size of the fixed header.
+ */
+ size_t total_packet_length;
+
+ /* scratch buffer to hold individual packets when they fragment across incoming data frame boundaries */
+ struct aws_byte_buf packet_buffer;
+};
+
+AWS_EXTERN_C_BEGIN
+
+/**
+ * Initialize function for the MQTT311 decoder
+ *
+ * @param decoder decoder to initialize
+ * @param allocator memory allocator to use
+ * @param options additional decoder configuration options
+ */
+AWS_MQTT_API void aws_mqtt311_decoder_init(
+ struct aws_mqtt311_decoder *decoder,
+ struct aws_allocator *allocator,
+ const struct aws_mqtt311_decoder_options *options);
+
+/**
+ * Clean up function for an MQTT311 decoder
+ *
+ * @param decoder decoder to release resources for
+ */
+AWS_MQTT_API void aws_mqtt311_decoder_clean_up(struct aws_mqtt311_decoder *decoder);
+
+/**
+ * Callback function to decode the incoming data stream of an MQTT311 connection. Handles packet framing and
+ * correct decoder/handler function dispatch.
+ *
+ * @param decoder decoder to decode with
+ * @param data raw plaintext bytes of a connection operating on the MQTT311 protocol
+ * @return success/failure, failure represents a protocol error and implies the connection must be shut down
+ */
+AWS_MQTT_API int aws_mqtt311_decoder_on_bytes_received(
+ struct aws_mqtt311_decoder *decoder,
+ struct aws_byte_cursor data);
+
+/**
+ * Resets a decoder's state to its initial values. If using a decoder across multiple network connections (within
+ * the same client), you must invoke this when setting up a new connection, before any MQTT protocol bytes are
+ * processed. Breaks the decoder out of any previous protocol error terminal state.
+ *
+ * @param decoder decoder to reset
+ */
+AWS_MQTT_API void aws_mqtt311_decoder_reset_for_new_connection(struct aws_mqtt311_decoder *decoder);
+
+AWS_EXTERN_C_END
+
+#endif /* AWS_MQTT_PRIVATE_MQTT311_DECODER_H */
diff --git a/contrib/restricted/aws/aws-c-mqtt/include/aws/mqtt/private/mqtt_client_test_helper.h b/contrib/restricted/aws/aws-c-mqtt/include/aws/mqtt/private/mqtt_client_test_helper.h
index 9dc2f129963..c3c430d2c1f 100644
--- a/contrib/restricted/aws/aws-c-mqtt/include/aws/mqtt/private/mqtt_client_test_helper.h
+++ b/contrib/restricted/aws/aws-c-mqtt/include/aws/mqtt/private/mqtt_client_test_helper.h
@@ -10,7 +10,7 @@
struct aws_allocator;
struct aws_byte_cursor;
-struct aws_mqtt_client_connection;
+struct aws_mqtt_client_connection_311_impl;
struct aws_string;
AWS_EXTERN_C_BEGIN
diff --git a/contrib/restricted/aws/aws-c-mqtt/include/aws/mqtt/private/mqtt_subscription_set.h b/contrib/restricted/aws/aws-c-mqtt/include/aws/mqtt/private/mqtt_subscription_set.h
new file mode 100644
index 00000000000..2ccb5d7e8ad
--- /dev/null
+++ b/contrib/restricted/aws/aws-c-mqtt/include/aws/mqtt/private/mqtt_subscription_set.h
@@ -0,0 +1,227 @@
+/**
+ * Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved.
+ * SPDX-License-Identifier: Apache-2.0.
+ */
+
+#ifndef AWS_MQTT_MQTT3_TO_MQTT5_ADAPTER_SUBSCRIPTION_SET_H
+#define AWS_MQTT_MQTT3_TO_MQTT5_ADAPTER_SUBSCRIPTION_SET_H
+
+#include "aws/mqtt/mqtt.h"
+
+#include "aws/mqtt/client.h"
+#include "aws/mqtt/v5/mqtt5_types.h"
+#include <aws/common/hash_table.h>
+
+/**
+ * (Transient) configuration options about a single persistent MQTT topic filter subscription
+ */
+struct aws_mqtt_subscription_set_subscription_options {
+ struct aws_byte_cursor topic_filter;
+
+ enum aws_mqtt5_qos qos;
+
+ bool no_local;
+ bool retain_as_published;
+ enum aws_mqtt5_retain_handling_type retain_handling_type;
+
+ /* Callback invoked when this subscription matches an incoming publish */
+ aws_mqtt_client_publish_received_fn *on_publish_received;
+
+ /* Callback invoked when this subscription is removed from the set */
+ aws_mqtt_userdata_cleanup_fn *on_cleanup;
+
+ void *callback_user_data;
+};
+
+/**
+ * Persistent structure to track a single MQTT topic filter subscription
+ */
+struct aws_mqtt_subscription_set_subscription_record {
+ struct aws_allocator *allocator;
+ struct aws_byte_buf topic_filter;
+
+ struct aws_mqtt_subscription_set_subscription_options subscription_view;
+};
+
+/**
+ * (Transient) configuration options about an incoming publish message
+ */
+struct aws_mqtt_subscription_set_publish_received_options {
+ struct aws_mqtt_client_connection *connection;
+
+ struct aws_byte_cursor topic;
+ enum aws_mqtt_qos qos;
+ bool retain;
+ bool dup;
+
+ struct aws_byte_cursor payload;
+};
+
+/**
+ * A node in the topic trie maintained by the subscription set. Each node represents a single "path segment" in a
+ * topic filter "path." Segments can be empty.
+ *
+ * Some examples (topic filter -> path segments):
+ *
+ * "hello/world" -> [ "hello", "world" ]
+ * "a/b/" -> [ "a", "b", "" ]
+ * "/b/" -> [ "", "b", "" ]
+ * "a/#/c" -> [ "a", "#", "c" ]
+ *
+ * On incoming publish, we walk the tree invoking callbacks based on topic vs. topic filter matching, segment by
+ * segment.
+ *
+ */
+struct aws_mqtt_subscription_set_topic_tree_node {
+ struct aws_allocator *allocator;
+
+ struct aws_byte_cursor topic_segment_cursor; /* segment can be empty */
+ struct aws_byte_buf topic_segment;
+
+ struct aws_mqtt_subscription_set_topic_tree_node *parent;
+ struct aws_hash_table children; /* (embedded topic_segment -> containing node) */
+
+ /*
+ * A node starts with a ref count of one and is incremented every time a new, overlapping path is added
+ * to the subscription set. When the ref count goes to zero, that means there are not subscriptions using the
+ * segment (or path suffix) represented by this node and therefor it can be deleted without any additional
+ * analysis.
+ *
+ * Replacing an existing path does not change the ref count.
+ */
+ size_t ref_count;
+
+ bool is_subscription;
+
+ aws_mqtt_client_publish_received_fn *on_publish_received;
+ aws_mqtt_userdata_cleanup_fn *on_cleanup;
+
+ void *callback_user_data;
+};
+
+/**
+ * Utility type to track a client's subscriptions.
+ *
+ * The topic tree supports per-subscription callbacks as used by the MQTT311 implementation.
+ *
+ * The subscriptions table supports resubscribe APIs for both MQTT311 and MQTT5 by tracking the subscription
+ * details on a per-topic-filter basis.
+ */
+struct aws_mqtt_subscription_set {
+ struct aws_allocator *allocator;
+
+ /* a permanent ref */
+ struct aws_mqtt_subscription_set_topic_tree_node *root;
+
+ /* embedded topic_filter_cursor -> persistent subscription */
+ struct aws_hash_table subscriptions;
+};
+
+AWS_EXTERN_C_BEGIN
+
+/**
+ * Creates a new subscription set
+ *
+ * @param allocator allocator to use
+ * @return a new subscription set or NULL
+ */
+AWS_MQTT_API struct aws_mqtt_subscription_set *aws_mqtt_subscription_set_new(struct aws_allocator *allocator);
+
+/**
+ * Destroys a subscription set
+ *
+ * @param subscription_set subscription set to destroy
+ */
+AWS_MQTT_API void aws_mqtt_subscription_set_destroy(struct aws_mqtt_subscription_set *subscription_set);
+
+/**
+ * Checks if a topic filter exists in the subscription set's hash table of subscriptions
+ *
+ * @param subscription_set subscription set to check
+ * @param topic_filter topic filter to check for existence in the set
+ * @return true if the topic filter exists in the table of subscriptions, false otherwise
+ */
+AWS_MQTT_API bool aws_mqtt_subscription_set_is_subscribed(
+ const struct aws_mqtt_subscription_set *subscription_set,
+ struct aws_byte_cursor topic_filter);
+
+/**
+ * Checks if a topic filter exists as a subscription (has a publish received handler) in the set's topic tree
+ *
+ * @param subscription_set subscription set to check
+ * @param topic_filter topic filter to check for existence in the set's topic tree
+ * @return true if the set's topic tree contains a publish received callback for the topic filter, false otherwise
+ */
+AWS_MQTT_API bool aws_mqtt_subscription_set_is_in_topic_tree(
+ const struct aws_mqtt_subscription_set *subscription_set,
+ struct aws_byte_cursor topic_filter);
+
+/**
+ * Adds a subscription to the subscription set. If a subscription already exists with a matching topic filter, it
+ * will be overwritten.
+ *
+ * @param subscription_set subscription set to add a subscription to
+ * @param subscription_options options for the new subscription
+ */
+AWS_MQTT_API void aws_mqtt_subscription_set_add_subscription(
+ struct aws_mqtt_subscription_set *subscription_set,
+ const struct aws_mqtt_subscription_set_subscription_options *subscription_options);
+
+/**
+ * Removes a subscription from the subscription set
+ *
+ * @param subscription_set subscription set to remove a subscription from
+ * @param topic_filter topic filter to remove subscription information for
+ */
+AWS_MQTT_API void aws_mqtt_subscription_set_remove_subscription(
+ struct aws_mqtt_subscription_set *subscription_set,
+ struct aws_byte_cursor topic_filter);
+
+/**
+ * Given a publish message, invokes all publish received handlers for matching subscriptions in the subscription set
+ *
+ * @param subscription_set subscription set to invoke publish received callbacks for
+ * @param publish_options received publish message properties
+ */
+AWS_MQTT_API void aws_mqtt_subscription_set_on_publish_received(
+ const struct aws_mqtt_subscription_set *subscription_set,
+ const struct aws_mqtt_subscription_set_publish_received_options *publish_options);
+
+/**
+ * Queries the properties of all subscriptions tracked by this subscription set. Used to implement re-subscribe
+ * behavior.
+ *
+ * @param subscription_set subscription set to query the subscriptions on
+ * @param subscriptions uninitialized array list to hold the subscriptions.
+ *
+ * The caller must invoke the cleanup function for array lists on the result. The list elements are of type
+ * 'struct aws_mqtt_subscription_set_subscription_options' and the topic filter cursor points to the subscription set's
+ * internal record. This means that the result must be used and cleaned up in local scope.
+ */
+AWS_MQTT_API void aws_mqtt_subscription_set_get_subscriptions(
+ struct aws_mqtt_subscription_set *subscription_set,
+ struct aws_array_list *subscriptions);
+
+/**
+ * Creates a new subscription record. A subscription record tracks all information about a single MQTT topic filter
+ * subscription
+ *
+ * @param allocator memory allocator to use
+ * @param subscription all relevant information about the subscription
+ * @return a new persistent subscription record
+ */
+AWS_MQTT_API struct aws_mqtt_subscription_set_subscription_record *aws_mqtt_subscription_set_subscription_record_new(
+ struct aws_allocator *allocator,
+ const struct aws_mqtt_subscription_set_subscription_options *subscription);
+
+/**
+ * Destroys a subscription record
+ *
+ * @param record subscription record to destroy
+ */
+AWS_MQTT_API void aws_mqtt_subscription_set_subscription_record_destroy(
+ struct aws_mqtt_subscription_set_subscription_record *record);
+
+AWS_EXTERN_C_END
+
+#endif /* AWS_MQTT_MQTT3_TO_MQTT5_ADAPTER_SUBSCRIPTION_SET_H */
diff --git a/contrib/restricted/aws/aws-c-mqtt/include/aws/mqtt/private/packets.h b/contrib/restricted/aws/aws-c-mqtt/include/aws/mqtt/private/packets.h
index 94a7591257f..c3b63ac5df8 100644
--- a/contrib/restricted/aws/aws-c-mqtt/include/aws/mqtt/private/packets.h
+++ b/contrib/restricted/aws/aws-c-mqtt/include/aws/mqtt/private/packets.h
@@ -220,6 +220,9 @@ AWS_MQTT_API
int aws_mqtt_packet_publish_decode(struct aws_byte_cursor *cur, struct aws_mqtt_packet_publish *packet);
AWS_MQTT_API
+void aws_mqtt_packet_publish_set_dup(struct aws_mqtt_packet_publish *packet);
+
+AWS_MQTT_API
bool aws_mqtt_packet_publish_get_dup(const struct aws_mqtt_packet_publish *packet);
AWS_MQTT_API
diff --git a/contrib/restricted/aws/aws-c-mqtt/include/aws/mqtt/private/v5/mqtt5_client_impl.h b/contrib/restricted/aws/aws-c-mqtt/include/aws/mqtt/private/v5/mqtt5_client_impl.h
index 7c08354963b..9a59de241dc 100644
--- a/contrib/restricted/aws/aws-c-mqtt/include/aws/mqtt/private/v5/mqtt5_client_impl.h
+++ b/contrib/restricted/aws/aws-c-mqtt/include/aws/mqtt/private/v5/mqtt5_client_impl.h
@@ -259,6 +259,12 @@ struct aws_mqtt5_client_operational_state {
struct aws_linked_list write_completion_operations;
/*
+ * heap of operation pointers where the timeout is the sort value. Elements are added/removed from this
+ * data structure in exact synchronization with unacked_operations_table.
+ */
+ struct aws_priority_queue operations_by_ack_timeout;
+
+ /*
* Is there an io message in transit (to the socket) that has not invoked its write completion callback yet?
* The client implementation only allows one in-transit message at a time, and so if this is true, we don't
* send additional ones/
@@ -342,7 +348,7 @@ struct aws_mqtt5_client {
/*
* Client configuration
*/
- const struct aws_mqtt5_client_options_storage *config;
+ struct aws_mqtt5_client_options_storage *config;
/*
* The recurrent task that runs all client logic outside of external event callbacks. Bound to the client's
@@ -489,6 +495,29 @@ struct aws_mqtt5_client {
* with clean start set to false.
*/
bool has_connected_successfully;
+
+ /*
+ * A flag that allows in-thread observers (currently the mqtt3_to_5 adapter) to signal that the connection
+ * should be torn down and re-established. Only relevant to the CONNECTING state which is not interruptible:
+ *
+ * If the mqtt5 client is in the CONNECTING state (ie waiting for bootstrap to complete) and the 3-adapter
+ * is asked to connect, then we *MUST* discard the in-progress connection attempt in order to guarantee the
+ * connection we establish uses all of the configuration parameters that are passed during the mqtt3 API's connect
+ * call (host, port, tls options, socket options, etc...). Since we can't interrupt the CONNECTING state, we
+ * instead set a flag that tells the mqtt5 client to tear down the connection as soon as the initial bootstrap
+ * completes. The reconnect will establish the requested connection using the parameters passed to
+ * the mqtt3 API.
+ *
+ * Rather than try and catch every escape path from CONNECTING, we lazily reset this flag to false when we
+ * enter the CONNECTING state. On a similar note, we only check this flag as we transition to MQTT_CONNECT.
+ *
+ * This flag is ultimately only needed when the 3 adapter and 5 client are used out-of-sync. If you use the
+ * 3 adapter exclusively after 5 client creation, it never comes into play.
+ *
+ * Even the adapter shouldn't manipulate this directly. Instead, use the aws_mqtt5_client_reset_connection private
+ * API to tear down an in-progress or established connection in response to a connect() request on the adapter.
+ */
+ bool should_reset_connection;
};
AWS_EXTERN_C_BEGIN
@@ -638,10 +667,44 @@ AWS_MQTT_API void aws_mqtt5_client_statistics_change_operation_statistic_state(
*/
AWS_MQTT_API const char *aws_mqtt5_client_state_to_c_string(enum aws_mqtt5_client_state state);
-/*
- * Temporary, private API to turn on total incoming packet logging at the byte level.
+/**
+ * An internal API used by the MQTT3 adapter to force any existing-or-in-progress connection to
+ * be torn down and re-established. Necessary because the MQTT3 interface allows overrides on a large number
+ * of configuration parameters through the connect() call. We must honor those parameters and the safest thing
+ * to do is to just throw away the current connection (if it exists) and make a new one. In the case that an MQTT5
+ * client is being driven entirely by the MQTT3 adapter, this case never actually happens.
+ *
+ * @param client client to reset an existing or in-progress connection for
+ * @return true if a connection reset was triggered, false if there was nothing to do
+ */
+AWS_MQTT_API bool aws_mqtt5_client_reset_connection(struct aws_mqtt5_client *client);
+
+/**
+ * Event-loop-internal API used to switch the client's desired state. Used by both start() and stop() cross-thread
+ * tasks as well as by the 3-to-5 adapter to make changes synchronously (when in the event loop).
+ *
+ * @param client mqtt5 client to update desired state for
+ * @param desired_state new desired state
+ * @param disconnect_op optional description of a DISCONNECT packet to send as part of a stop command
+ */
+AWS_MQTT_API void aws_mqtt5_client_change_desired_state(
+ struct aws_mqtt5_client *client,
+ enum aws_mqtt5_client_state desired_state,
+ struct aws_mqtt5_operation_disconnect *disconnect_op);
+
+/**
+ * Event-loop-internal API to add an operation to the client's queue. Used by the 3-to-5 adapter to synchnrously
+ * inject the MQTT5 operation once the adapter operation has reached the event loop.
+ *
+ * @param client MQTT5 client to submit an operation to
+ * @param operation MQTT5 operation to submit
+ * @param is_terminated flag that indicates whether the submitter is shutting down or not. Needed to differentiate
+ * between adapter submissions and MQTT5 client API submissions and correctly handle ref count adjustments.
*/
-AWS_MQTT_API void aws_mqtt5_client_enable_full_packet_logging(struct aws_mqtt5_client *client);
+AWS_MQTT_API void aws_mqtt5_client_submit_operation_internal(
+ struct aws_mqtt5_client *client,
+ struct aws_mqtt5_operation *operation,
+ bool is_terminated);
AWS_EXTERN_C_END
diff --git a/contrib/restricted/aws/aws-c-mqtt/include/aws/mqtt/private/v5/mqtt5_options_storage.h b/contrib/restricted/aws/aws-c-mqtt/include/aws/mqtt/private/v5/mqtt5_options_storage.h
index dcc07d399d4..ddcb0ab5923 100644
--- a/contrib/restricted/aws/aws-c-mqtt/include/aws/mqtt/private/v5/mqtt5_options_storage.h
+++ b/contrib/restricted/aws/aws-c-mqtt/include/aws/mqtt/private/v5/mqtt5_options_storage.h
@@ -12,6 +12,7 @@
#include <aws/common/logging.h>
#include <aws/common/ref_count.h>
#include <aws/http/proxy.h>
+#include <aws/io/host_resolver.h>
#include <aws/io/retry_strategy.h>
#include <aws/io/socket.h>
#include <aws/io/tls_channel_handler.h>
@@ -41,6 +42,8 @@ struct aws_mqtt5_operation_vtable {
int (*aws_mqtt5_operation_validate_vs_connection_settings_fn)(
const void *operation_packet_view,
const struct aws_mqtt5_client *client);
+
+ uint32_t (*aws_mqtt5_operation_get_ack_timeout_override_fn)(const struct aws_mqtt5_operation *operation);
};
/* Flags that indicate the way in which an operation is currently affecting the statistics of the client */
@@ -63,6 +66,7 @@ struct aws_mqtt5_operation {
const struct aws_mqtt5_operation_vtable *vtable;
struct aws_ref_count ref_count;
uint64_t ack_timeout_timepoint_ns;
+ struct aws_priority_queue_node priority_queue_node;
struct aws_linked_list_node node;
enum aws_mqtt5_packet_type packet_type;
@@ -137,7 +141,7 @@ struct aws_mqtt5_client_options_storage {
struct aws_allocator *allocator;
struct aws_string *host_name;
- uint16_t port;
+ uint32_t port;
struct aws_client_bootstrap *bootstrap;
struct aws_socket_options socket_options;
@@ -162,20 +166,22 @@ struct aws_mqtt5_client_options_storage {
uint64_t max_reconnect_delay_ms;
uint64_t min_connected_time_to_reset_reconnect_delay_ms;
- uint64_t ack_timeout_seconds;
+ uint32_t ack_timeout_seconds;
uint32_t ping_timeout_ms;
uint32_t connack_timeout_ms;
struct aws_mqtt5_client_topic_alias_options topic_aliasing_options;
- struct aws_mqtt5_packet_connect_storage connect;
+ struct aws_mqtt5_packet_connect_storage *connect;
aws_mqtt5_client_connection_event_callback_fn *lifecycle_event_handler;
void *lifecycle_event_handler_user_data;
aws_mqtt5_client_termination_completion_fn *client_termination_handler;
void *client_termination_handler_user_data;
+
+ struct aws_host_resolution_config host_resolution_override;
};
AWS_EXTERN_C_BEGIN
@@ -205,6 +211,8 @@ AWS_MQTT_API int aws_mqtt5_operation_validate_vs_connection_settings(
const struct aws_mqtt5_operation *operation,
const struct aws_mqtt5_client *client);
+AWS_MQTT_API uint32_t aws_mqtt5_operation_get_ack_timeout_override(const struct aws_mqtt5_operation *operation);
+
/* Connect */
AWS_MQTT_API struct aws_mqtt5_operation_connect *aws_mqtt5_operation_connect_new(
@@ -254,9 +262,6 @@ AWS_MQTT_API struct aws_mqtt5_operation_publish *aws_mqtt5_operation_publish_new
AWS_MQTT_API int aws_mqtt5_packet_publish_view_validate(const struct aws_mqtt5_packet_publish_view *publish_view);
-AWS_MQTT_API int aws_mqtt5_packet_publish_view_validate_vs_iot_core(
- const struct aws_mqtt5_packet_publish_view *publish_view);
-
AWS_MQTT_API void aws_mqtt5_packet_publish_view_log(
const struct aws_mqtt5_packet_publish_view *publish_view,
enum aws_log_level level);
@@ -281,9 +286,6 @@ AWS_MQTT_API struct aws_mqtt5_operation_subscribe *aws_mqtt5_operation_subscribe
AWS_MQTT_API int aws_mqtt5_packet_subscribe_view_validate(const struct aws_mqtt5_packet_subscribe_view *subscribe_view);
-AWS_MQTT_API int aws_mqtt5_packet_subscribe_view_validate_vs_iot_core(
- const struct aws_mqtt5_packet_subscribe_view *subscribe_view);
-
AWS_MQTT_API void aws_mqtt5_packet_subscribe_view_log(
const struct aws_mqtt5_packet_subscribe_view *subscribe_view,
enum aws_log_level level);
@@ -305,9 +307,6 @@ AWS_MQTT_API struct aws_mqtt5_operation_unsubscribe *aws_mqtt5_operation_unsubsc
AWS_MQTT_API int aws_mqtt5_packet_unsubscribe_view_validate(
const struct aws_mqtt5_packet_unsubscribe_view *unsubscribe_view);
-AWS_MQTT_API int aws_mqtt5_packet_unsubscribe_view_validate_vs_iot_core(
- const struct aws_mqtt5_packet_unsubscribe_view *unsubscribe_view);
-
AWS_MQTT_API void aws_mqtt5_packet_unsubscribe_view_log(
const struct aws_mqtt5_packet_unsubscribe_view *unsubscribe_view,
enum aws_log_level level);
diff --git a/contrib/restricted/aws/aws-c-mqtt/include/aws/mqtt/private/v5/mqtt5_to_mqtt3_adapter_impl.h b/contrib/restricted/aws/aws-c-mqtt/include/aws/mqtt/private/v5/mqtt5_to_mqtt3_adapter_impl.h
new file mode 100644
index 00000000000..d892d07159c
--- /dev/null
+++ b/contrib/restricted/aws/aws-c-mqtt/include/aws/mqtt/private/v5/mqtt5_to_mqtt3_adapter_impl.h
@@ -0,0 +1,341 @@
+/**
+ * Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved.
+ * SPDX-License-Identifier: Apache-2.0.
+ */
+
+#ifndef AWS_MQTT_MQTT5_TO_MQTT3_ADAPTER_IMPL_H
+#define AWS_MQTT_MQTT5_TO_MQTT3_ADAPTER_IMPL_H
+
+#include <aws/mqtt/mqtt.h>
+
+#include <aws/common/mutex.h>
+#include <aws/common/rw_lock.h>
+#include <aws/common/task_scheduler.h>
+#include <aws/mqtt/client.h>
+#include <aws/mqtt/private/client_impl_shared.h>
+#include <aws/mqtt/v5/mqtt5_client.h>
+
+struct aws_mqtt_subscription_set;
+
+struct aws_mqtt5_to_mqtt3_adapter_publish_options {
+ struct aws_mqtt_client_connection_5_impl *adapter;
+
+ const struct aws_byte_cursor topic;
+ enum aws_mqtt_qos qos;
+ bool retain;
+ const struct aws_byte_cursor payload;
+
+ aws_mqtt_op_complete_fn *on_complete;
+ void *on_complete_userdata;
+};
+
+/*
+ * A subscribe with no subscriptions represents a re-subscribe of all internally tracked topics. While this
+ * is a bit hacky, the alternative is to copy-and-paste almost the entire multi-subscribe adapter operation and
+ * supporting logic, which is approximately 300 lines.
+ */
+struct aws_mqtt5_to_mqtt3_adapter_subscribe_options {
+ struct aws_mqtt_client_connection_5_impl *adapter;
+
+ struct aws_mqtt_topic_subscription *subscriptions;
+ size_t subscription_count;
+
+ aws_mqtt_suback_fn *on_suback;
+ void *on_suback_user_data;
+
+ aws_mqtt_suback_multi_fn *on_multi_suback;
+ void *on_multi_suback_user_data;
+};
+
+struct aws_mqtt5_to_mqtt3_adapter_unsubscribe_options {
+ struct aws_mqtt_client_connection_5_impl *adapter;
+
+ struct aws_byte_cursor topic_filter;
+
+ aws_mqtt_op_complete_fn *on_unsuback;
+ void *on_unsuback_user_data;
+};
+
+enum aws_mqtt5_to_mqtt3_adapter_operation_type {
+ AWS_MQTT5TO3_AOT_PUBLISH,
+ AWS_MQTT5TO3_AOT_SUBSCRIBE,
+ AWS_MQTT5TO3_AOT_UNSUBSCRIBE,
+};
+
+struct aws_mqtt5_to_mqtt3_adapter_operation_vtable {
+ void (*fail_fn)(void *impl, int error_code);
+};
+
+struct aws_mqtt5_to_mqtt3_adapter_operation_base {
+ struct aws_allocator *allocator;
+ struct aws_ref_count ref_count;
+ const struct aws_mqtt5_to_mqtt3_adapter_operation_vtable *vtable;
+
+ void *impl;
+
+ /*
+ * Holds an internal reference to the adapter while traveling to the event loop. Reference gets released
+ * after intake on the event loop.
+ *
+ * We avoid calling back into a deleted adapter by zeroing out the
+ * mqtt5 operation callbacks for everything we've submitted before final mqtt5 client release.
+ */
+ struct aws_mqtt_client_connection_5_impl *adapter;
+ bool holding_adapter_ref;
+
+ struct aws_task submission_task;
+
+ enum aws_mqtt5_to_mqtt3_adapter_operation_type type;
+ uint16_t id;
+};
+
+struct aws_mqtt5_to_mqtt3_adapter_operation_publish {
+ struct aws_mqtt5_to_mqtt3_adapter_operation_base base;
+
+ /*
+ * holds a reference to the MQTT5 client publish operation until the operation completes or our adapter
+ * goes away.
+ *
+ * In the case where we're going away, we zero out the MQTT5 operation callbacks to prevent crash-triggering
+ * notifications.
+ */
+ struct aws_mqtt5_operation_publish *publish_op;
+
+ aws_mqtt_op_complete_fn *on_publish_complete;
+ void *on_publish_complete_user_data;
+};
+
+struct aws_mqtt5_to_mqtt3_adapter_operation_subscribe {
+ struct aws_mqtt5_to_mqtt3_adapter_operation_base base;
+
+ /*
+ * holds a reference to the MQTT5 client subscribe operation until the operation completes or our adapter
+ * goes away.
+ *
+ * In the case where we're going away, we zero out the MQTT5 operation callbacks to prevent crash-triggering
+ * notifications.
+ */
+ struct aws_mqtt5_operation_subscribe *subscribe_op;
+
+ /* aws_array_list<struct aws_mqtt_subscription_set_subscription_record *> */
+ struct aws_array_list subscriptions;
+
+ aws_mqtt_suback_fn *on_suback;
+ void *on_suback_user_data;
+
+ aws_mqtt_suback_multi_fn *on_multi_suback;
+ void *on_multi_suback_user_data;
+};
+
+struct aws_mqtt5_to_mqtt3_adapter_operation_unsubscribe {
+ struct aws_mqtt5_to_mqtt3_adapter_operation_base base;
+
+ /*
+ * holds a reference to the MQTT5 client unsubscribe operation until the operation completes or our adapter
+ * goes away.
+ *
+ * In the case where we're going away, we zero out the MQTT5 operation callbacks to prevent crash-triggering
+ * notifications.
+ */
+ struct aws_mqtt5_operation_unsubscribe *unsubscribe_op;
+
+ struct aws_byte_buf topic_filter;
+
+ aws_mqtt_op_complete_fn *on_unsuback;
+ void *on_unsuback_user_data;
+};
+
+/*
+
+ Sequencing (PUBLISH example):
+
+ Mqtt311 public API call
+ Create cross thread task
+ Create adapter op -> Create and attach mqtt5 op
+ allocate id and add operation to adapter table
+ Add adapter op's internal ref to adapter
+ submit cross thread task to event loop
+ return id or 0
+
+ Adapter Op reaches event loop task function: (from this point, all callbacks must be safe-guarded)
+ terminated = true
+ Safe handler:
+ If adapter not terminated:
+ terminated = false
+ Synchronously enqueue operation to mqtt5 client
+ if terminated:
+ remove adapter op from table
+ destroy adapter op
+ Release adapter op's internal ref to adapter
+
+ On publish completion:
+ Safe handler:
+ If not terminated:
+ invoke mqtt311 callback
+ Remove adapter op from table
+ Destroy adapter op
+
+ On final destroy (zero internal refs):
+ Iterate all incomplete adapter operations and cancel them: zero callbacks and remove from queue if in queue and
+ unbound
+ Destroy all adapter ops
+ Clear table
+*/
+
+struct aws_mqtt5_to_mqtt3_adapter_operation_table {
+ struct aws_mutex lock;
+
+ struct aws_hash_table operations;
+ uint16_t next_id;
+};
+
+/*
+ * The adapter maintains a notion of state based on how its 311 API has been used. This state guides how it handles
+ * external lifecycle events.
+ *
+ * Operational (sourced from the adapter) events are always relayed unless the adapter has been terminated.
+ */
+enum aws_mqtt_adapter_state {
+
+ /*
+ * The 311 API has had connect() called but that connect has not yet resolved.
+ *
+ * If it resolves successfully we will move to the STAY_CONNECTED state which will relay lifecycle callbacks
+ * transparently.
+ *
+ * If it resolves unsuccessfully, we will move to the STAY_DISCONNECTED state where we will ignore lifecycle
+ * events because, from the 311 API's perspective, nothing should be getting emitted.
+ */
+ AWS_MQTT_AS_FIRST_CONNECT,
+
+ /*
+ * A call to the 311 connect API has resolved successfully. Relay all lifecycle events until told otherwise.
+ */
+ AWS_MQTT_AS_STAY_CONNECTED,
+
+ /*
+ * We have not observed a successful initial connection attempt via the 311 API (or disconnect has been
+ * invoked afterwards). Ignore all lifecycle events.
+ */
+ AWS_MQTT_AS_STAY_DISCONNECTED,
+};
+
+struct aws_mqtt_client_connection_5_impl {
+
+ struct aws_allocator *allocator;
+
+ struct aws_mqtt_client_connection base;
+
+ struct aws_mqtt5_client *client;
+ struct aws_mqtt5_listener *listener;
+ struct aws_event_loop *loop;
+
+ /*
+ * The current adapter state based on the sequence of connect(), disconnect(), and connection completion events.
+ * This affects how the adapter reacts to incoming mqtt5 events. Under certain conditions, we may change
+ * this state value based on unexpected events (stopping the mqtt5 client underneath the adapter, for example)
+ */
+ enum aws_mqtt_adapter_state adapter_state;
+
+ /*
+ * Tracks all references from external sources (ie users). Incremented and decremented by the public
+ * acquire/release APIs of the 311 connection.
+ *
+ * When this value drops to zero, the terminated flag is set and no further callbacks will be invoked. This
+ * also starts the asynchronous destruction process for the adapter.
+ */
+ struct aws_ref_count external_refs;
+
+ /*
+ * Tracks all references to the adapter from internal sources (temporary async processes that need the
+ * adapter to stay alive for an interval of time, like sending tasks across thread boundaries).
+ *
+ * Starts with a single reference that is held until the adapter's listener has fully detached from the mqtt5
+ * client.
+ *
+ * Once the internal ref count drops to zero, the adapter may be destroyed synchronously.
+ */
+ struct aws_ref_count internal_refs;
+
+ struct aws_mqtt5_to_mqtt3_adapter_operation_table operational_state;
+
+ struct aws_mqtt_subscription_set *subscriptions;
+
+ /* All fields after here are internal to the adapter event loop thread */
+
+ /* 311 interface callbacks */
+ aws_mqtt_client_on_connection_interrupted_fn *on_interrupted;
+ void *on_interrupted_user_data;
+
+ aws_mqtt_client_on_connection_resumed_fn *on_resumed;
+ void *on_resumed_user_data;
+
+ aws_mqtt_client_on_connection_closed_fn *on_closed;
+ void *on_closed_user_data;
+
+ aws_mqtt_client_on_connection_success_fn *on_connection_success;
+ void *on_connection_success_user_data;
+
+ aws_mqtt_client_on_connection_failure_fn *on_connection_failure;
+ void *on_connection_failure_user_data;
+
+ aws_mqtt_client_publish_received_fn *on_any_publish;
+ void *on_any_publish_user_data;
+
+ aws_mqtt_transform_websocket_handshake_fn *websocket_handshake_transformer;
+ void *websocket_handshake_transformer_user_data;
+
+ aws_mqtt5_transform_websocket_handshake_complete_fn *mqtt5_websocket_handshake_completion_function;
+ void *mqtt5_websocket_handshake_completion_user_data;
+
+ /* (mutually exclusive) 311 interface one-time transient callbacks */
+ aws_mqtt_client_on_disconnect_fn *on_disconnect;
+ void *on_disconnect_user_data;
+
+ aws_mqtt_client_on_connection_complete_fn *on_connection_complete;
+ void *on_connection_complete_user_data;
+
+ aws_mqtt_client_on_connection_termination_fn *on_termination;
+ void *on_termination_user_data;
+};
+
+AWS_EXTERN_C_BEGIN
+
+AWS_MQTT_API void aws_mqtt5_to_mqtt3_adapter_operation_table_init(
+ struct aws_mqtt5_to_mqtt3_adapter_operation_table *table,
+ struct aws_allocator *allocator);
+
+AWS_MQTT_API void aws_mqtt5_to_mqtt3_adapter_operation_table_clean_up(
+ struct aws_mqtt5_to_mqtt3_adapter_operation_table *table);
+
+AWS_MQTT_API int aws_mqtt5_to_mqtt3_adapter_operation_table_add_operation(
+ struct aws_mqtt5_to_mqtt3_adapter_operation_table *table,
+ struct aws_mqtt5_to_mqtt3_adapter_operation_base *operation);
+
+AWS_MQTT_API void aws_mqtt5_to_mqtt3_adapter_operation_table_remove_operation(
+ struct aws_mqtt5_to_mqtt3_adapter_operation_table *table,
+ uint16_t operation_id);
+
+AWS_MQTT_API struct aws_mqtt5_to_mqtt3_adapter_operation_publish *aws_mqtt5_to_mqtt3_adapter_operation_new_publish(
+ struct aws_allocator *allocator,
+ const struct aws_mqtt5_to_mqtt3_adapter_publish_options *options);
+
+AWS_MQTT_API struct aws_mqtt5_to_mqtt3_adapter_operation_subscribe *aws_mqtt5_to_mqtt3_adapter_operation_new_subscribe(
+ struct aws_allocator *allocator,
+ const struct aws_mqtt5_to_mqtt3_adapter_subscribe_options *options,
+ struct aws_mqtt_client_connection_5_impl *adapter);
+
+AWS_MQTT_API struct aws_mqtt5_to_mqtt3_adapter_operation_unsubscribe *
+ aws_mqtt5_to_mqtt3_adapter_operation_new_unsubscribe(
+ struct aws_allocator *allocator,
+ const struct aws_mqtt5_to_mqtt3_adapter_unsubscribe_options *options);
+
+AWS_MQTT_API struct aws_mqtt5_to_mqtt3_adapter_operation_base *aws_mqtt5_to_mqtt3_adapter_operation_release(
+ struct aws_mqtt5_to_mqtt3_adapter_operation_base *operation);
+
+AWS_MQTT_API struct aws_mqtt5_to_mqtt3_adapter_operation_base *aws_mqtt5_to_mqtt3_adapter_operation_acquire(
+ struct aws_mqtt5_to_mqtt3_adapter_operation_base *operation);
+
+AWS_EXTERN_C_END
+
+#endif /* AWS_MQTT_MQTT5_TO_MQTT3_ADAPTER_IMPL_H */
diff --git a/contrib/restricted/aws/aws-c-mqtt/include/aws/mqtt/private/v5/mqtt5_utils.h b/contrib/restricted/aws/aws-c-mqtt/include/aws/mqtt/private/v5/mqtt5_utils.h
index be4c8ba2cf8..697a433099b 100644
--- a/contrib/restricted/aws/aws-c-mqtt/include/aws/mqtt/private/v5/mqtt5_utils.h
+++ b/contrib/restricted/aws/aws-c-mqtt/include/aws/mqtt/private/v5/mqtt5_utils.h
@@ -67,10 +67,8 @@ struct aws_mqtt5_negotiated_settings;
#define AWS_MQTT5_SUBSCRIBE_FLAGS_QOS_BIT_MASK 0x03
/* Static AWS IoT Core Limit/Quota Values */
-#define AWS_IOT_CORE_MAXIMUM_CLIENT_ID_LENGTH 128
#define AWS_IOT_CORE_MAXIMUM_TOPIC_LENGTH 256
#define AWS_IOT_CORE_MAXIMUM_TOPIC_SEGMENTS 8
-#define AWS_IOT_CORE_MAXIMUM_SUSBCRIPTIONS_PER_SUBSCRIBE 8
/* Dynamic IoT Core Limits */
#define AWS_IOT_CORE_PUBLISH_PER_SECOND_LIMIT 100
@@ -97,14 +95,6 @@ AWS_EXTERN_C_BEGIN
AWS_MQTT_API extern struct aws_byte_cursor g_aws_mqtt5_connect_protocol_cursor;
/**
- * Validate utf-8 string under mqtt5 specs
- *
- * @param text
- * @return AWS_OP_SUCCESS if the text is validate, otherwise AWS_OP_ERR
- */
-AWS_MQTT_API int aws_mqtt5_validate_utf8_text(struct aws_byte_cursor text);
-
-/**
* Simple helper function to compute the first byte of an MQTT packet encoding as a function of 4 bit flags
* and the packet type.
*
@@ -221,6 +211,15 @@ AWS_MQTT_API const char *aws_mqtt5_outbound_topic_alias_behavior_type_to_c_strin
enum aws_mqtt5_client_outbound_topic_alias_behavior_type outbound_aliasing_behavior);
/**
+ * Checks an outbound aliasing behavior type value for validity
+ *
+ * @param outbound_aliasing_behavior value to check
+ * @return true if this is a valid value, false otherwise
+ */
+AWS_MQTT_API bool aws_mqtt5_outbound_topic_alias_behavior_type_validate(
+ enum aws_mqtt5_client_outbound_topic_alias_behavior_type outbound_aliasing_behavior);
+
+/**
* Converts an outbound topic aliasing behavior type value to a final non-default value.
*
* @param outbound_aliasing_behavior type of outbound topic aliasing behavior
@@ -240,6 +239,15 @@ AWS_MQTT_API const char *aws_mqtt5_inbound_topic_alias_behavior_type_to_c_string
enum aws_mqtt5_client_inbound_topic_alias_behavior_type inbound_aliasing_behavior);
/**
+ * Checks an inbound aliasing behavior type value for validity
+ *
+ * @param inbound_aliasing_behavior value to check
+ * @return true if this is a valid value, false otherwise
+ */
+AWS_MQTT_API bool aws_mqtt5_inbound_topic_alias_behavior_type_validate(
+ enum aws_mqtt5_client_inbound_topic_alias_behavior_type inbound_aliasing_behavior);
+
+/**
* Converts an inbound topic aliasing behavior type value to a final non-default value.
*
* @param inbound_aliasing_behavior type of inbound topic aliasing behavior
@@ -328,7 +336,8 @@ AWS_MQTT_API uint64_t aws_mqtt5_client_random_in_range(uint64_t from, uint64_t t
* @param topic_cursor topic to get the non-rules suffix for
* @return remaining part of the topic after the leading AWS IoT Rules prefix has been skipped, if present
*/
-AWS_MQTT_API struct aws_byte_cursor aws_mqtt5_topic_skip_aws_iot_rules_prefix(struct aws_byte_cursor topic_cursor);
+AWS_MQTT_API struct aws_byte_cursor aws_mqtt5_topic_skip_aws_iot_core_uncounted_prefix(
+ struct aws_byte_cursor topic_cursor);
/**
* Computes the number of topic segments in a topic or topic filter
diff --git a/contrib/restricted/aws/aws-c-mqtt/include/aws/mqtt/v5/mqtt5_client.h b/contrib/restricted/aws/aws-c-mqtt/include/aws/mqtt/v5/mqtt5_client.h
index e99338cee25..d04d29814b0 100644
--- a/contrib/restricted/aws/aws-c-mqtt/include/aws/mqtt/v5/mqtt5_client.h
+++ b/contrib/restricted/aws/aws-c-mqtt/include/aws/mqtt/v5/mqtt5_client.h
@@ -6,23 +6,17 @@
* SPDX-License-Identifier: Apache-2.0.
*/
-/**
- * DEVELOPER PREVIEW DISCLAIMER
- *
- * MQTT5 support is currently in **developer preview**. We encourage feedback at all times, but feedback during the
- * preview window is especially valuable in shaping the final product. During the preview period we may make
- * backwards-incompatible changes to the public API, but in general, this is something we will try our best to avoid.
- */
-
#include <aws/mqtt/mqtt.h>
#include <aws/io/retry_strategy.h>
#include <aws/mqtt/v5/mqtt5_types.h>
+AWS_PUSH_SANE_WARNING_LEVEL
+
struct aws_allocator;
struct aws_client_bootstrap;
+struct aws_host_resolution_config;
struct aws_http_message;
-struct aws_input_stream;
struct aws_mqtt5_client;
struct aws_mqtt5_client_lifecycle_event;
struct aws_tls_connection_options;
@@ -84,16 +78,15 @@ enum aws_mqtt5_client_outbound_topic_alias_behavior_type {
* topic alias mappings unpredictably. The client will properly use the alias when the current connection
* has seen the alias binding already.
*/
- AWS_MQTT5_COTABT_USER,
+ AWS_MQTT5_COTABT_MANUAL,
/**
- * Client fails any user-specified topic aliasing and acts on the outbound alias set as an LRU cache.
+ * Client ignores any user-specified topic aliasing and acts on the outbound alias set as an LRU cache.
*/
AWS_MQTT5_COTABT_LRU,
/**
- * Completely disable outbound topic aliasing. Attempting to set a topic alias on a PUBLISH results in
- * an error.
+ * Completely disable outbound topic aliasing.
*/
AWS_MQTT5_COTABT_DISABLED
};
@@ -160,7 +153,7 @@ struct aws_mqtt5_client_topic_alias_options {
* disabled, this setting has no effect.
*
* Behaviorally, this value overrides anything present in the topic_alias_maximum field of
- * the CONNECT packet options. We intentionally don't bind that field to managed clients to reduce
+ * the CONNECT packet options.
*/
uint16_t inbound_alias_cache_size;
};
@@ -179,16 +172,10 @@ enum aws_mqtt5_extended_validation_and_flow_control_options {
AWS_MQTT5_EVAFCO_NONE,
/**
- * Apply additional client-side validation and operational flow control that respects the
+ * Apply additional client-side operational flow control that respects the
* default AWS IoT Core limits.
*
- * Currently applies the following additional validation:
- * (1) No more than 8 subscriptions per SUBSCRIBE packet
- * (2) Topics and topic filters have a maximum of 7 slashes (8 segments), not counting any AWS rules prefix
- * (3) Topics must be <= 256 bytes in length
- * (4) Client id must be <= 128 bytes in length
- *
- * Also applies the following flow control:
+ * Applies the following flow control:
* (1) Outbound throughput throttled to 512KB/s
* (2) Outbound publish TPS throttled to 100
*/
@@ -348,31 +335,37 @@ typedef void(aws_mqtt5_client_termination_completion_fn)(void *complete_ctx);
/* operation completion options structures */
/**
- * Completion callback options for the Publish operation
+ * Completion options for the Publish operation
*/
struct aws_mqtt5_publish_completion_options {
aws_mqtt5_publish_completion_fn *completion_callback;
void *completion_user_data;
+
+ uint32_t ack_timeout_seconds_override;
};
/**
- * Completion callback options for the Subscribe operation
+ * Completion options for the Subscribe operation
*/
struct aws_mqtt5_subscribe_completion_options {
aws_mqtt5_subscribe_completion_fn *completion_callback;
void *completion_user_data;
+
+ uint32_t ack_timeout_seconds_override;
};
/**
- * Completion callback options for the Unsubscribe operation
+ * Completion options for the Unsubscribe operation
*/
struct aws_mqtt5_unsubscribe_completion_options {
aws_mqtt5_unsubscribe_completion_fn *completion_callback;
void *completion_user_data;
+
+ uint32_t ack_timeout_seconds_override;
};
/**
- * Public completion callback options for the a DISCONNECT operation
+ * Completion options for the a DISCONNECT operation
*/
struct aws_mqtt5_disconnect_completion_options {
aws_mqtt5_disconnect_completion_fn *completion_callback;
@@ -533,7 +526,7 @@ struct aws_mqtt5_client_options {
/**
* Port to establish mqtt connections to
*/
- uint16_t port;
+ uint32_t port;
/**
* Client bootstrap to use whenever this client establishes a connection
@@ -629,7 +622,7 @@ struct aws_mqtt5_client_options {
/**
* Controls how the client uses mqtt5 topic aliasing. If NULL, zero-based defaults will be used.
*/
- struct aws_mqtt5_client_topic_alias_options *topic_aliasing_options;
+ const struct aws_mqtt5_client_topic_alias_options *topic_aliasing_options;
/**
* Callback for received publish packets
@@ -655,6 +648,12 @@ struct aws_mqtt5_client_options {
*/
aws_mqtt5_client_termination_completion_fn *client_termination_handler;
void *client_termination_handler_user_data;
+
+ /**
+ * Options to override aspects of DNS resolution. If unspecified, use a default that matches the regular
+ * configuration but changes the refresh frequency to a value that prevents DNS pinging.
+ */
+ struct aws_host_resolution_config *host_resolution_override;
};
AWS_EXTERN_C_BEGIN
@@ -786,12 +785,14 @@ AWS_MQTT_API int aws_mqtt5_negotiated_settings_init(
const struct aws_byte_cursor *client_id);
/**
- * Makes an owning copy of a negotiated settings structure
+ * Makes an owning copy of a negotiated settings structure.
*
* @param source settings to copy from
* @param dest settings to copy into. Must be in a zeroed or initialized state because it gets clean up
* called on it as the first step of the copy process.
* @return success/failure
+ *
+ * Used in downstream.
*/
AWS_MQTT_API int aws_mqtt5_negotiated_settings_copy(
const struct aws_mqtt5_negotiated_settings *source,
@@ -805,5 +806,6 @@ AWS_MQTT_API int aws_mqtt5_negotiated_settings_copy(
AWS_MQTT_API void aws_mqtt5_negotiated_settings_clean_up(struct aws_mqtt5_negotiated_settings *negotiated_settings);
AWS_EXTERN_C_END
+AWS_POP_SANE_WARNING_LEVEL
#endif /* AWS_MQTT_MQTT5_CLIENT_H */
diff --git a/contrib/restricted/aws/aws-c-mqtt/include/aws/mqtt/v5/mqtt5_listener.h b/contrib/restricted/aws/aws-c-mqtt/include/aws/mqtt/v5/mqtt5_listener.h
index 8d0498cebd8..7eb9664c305 100644
--- a/contrib/restricted/aws/aws-c-mqtt/include/aws/mqtt/v5/mqtt5_listener.h
+++ b/contrib/restricted/aws/aws-c-mqtt/include/aws/mqtt/v5/mqtt5_listener.h
@@ -10,6 +10,8 @@
#include <aws/mqtt/v5/mqtt5_client.h>
+AWS_PUSH_SANE_WARNING_LEVEL
+
/*
* Callback signature for when an mqtt5 listener has completely destroyed itself.
*/
@@ -81,5 +83,6 @@ AWS_MQTT_API struct aws_mqtt5_listener *aws_mqtt5_listener_acquire(struct aws_mq
AWS_MQTT_API struct aws_mqtt5_listener *aws_mqtt5_listener_release(struct aws_mqtt5_listener *listener);
AWS_EXTERN_C_END
+AWS_POP_SANE_WARNING_LEVEL
#endif /* AWS_MQTT_MQTT5_LISTENER_H */
diff --git a/contrib/restricted/aws/aws-c-mqtt/include/aws/mqtt/v5/mqtt5_packet_storage.h b/contrib/restricted/aws/aws-c-mqtt/include/aws/mqtt/v5/mqtt5_packet_storage.h
index 9a7028f4595..354990c8e33 100644
--- a/contrib/restricted/aws/aws-c-mqtt/include/aws/mqtt/v5/mqtt5_packet_storage.h
+++ b/contrib/restricted/aws/aws-c-mqtt/include/aws/mqtt/v5/mqtt5_packet_storage.h
@@ -6,18 +6,12 @@
* SPDX-License-Identifier: Apache-2.0.
*/
-/**
- * DEVELOPER PREVIEW DISCLAIMER
- *
- * MQTT5 support is currently in **developer preview**. We encourage feedback at all times, but feedback during the
- * preview window is especially valuable in shaping the final product. During the preview period we may make
- * backwards-incompatible changes to the public API, but in general, this is something we will try our best to avoid.
- */
-
#include <aws/mqtt/mqtt.h>
#include <aws/mqtt/v5/mqtt5_types.h>
+AWS_PUSH_SANE_WARNING_LEVEL
+
struct aws_mqtt5_user_property_set {
struct aws_array_list properties;
};
@@ -332,5 +326,6 @@ AWS_MQTT_API void aws_mqtt5_packet_unsuback_storage_clean_up(
struct aws_mqtt5_packet_unsuback_storage *unsuback_storage);
AWS_EXTERN_C_END
+AWS_POP_SANE_WARNING_LEVEL
#endif /* AWS_MQTT_MQTT5_PACKET_STORAGE_H */
diff --git a/contrib/restricted/aws/aws-c-mqtt/include/aws/mqtt/v5/mqtt5_types.h b/contrib/restricted/aws/aws-c-mqtt/include/aws/mqtt/v5/mqtt5_types.h
index f8db39516b2..b9ff8464024 100644
--- a/contrib/restricted/aws/aws-c-mqtt/include/aws/mqtt/v5/mqtt5_types.h
+++ b/contrib/restricted/aws/aws-c-mqtt/include/aws/mqtt/v5/mqtt5_types.h
@@ -6,19 +6,13 @@
* SPDX-License-Identifier: Apache-2.0.
*/
-/**
- * DEVELOPER PREVIEW DISCLAIMER
- *
- * MQTT5 support is currently in **developer preview**. We encourage feedback at all times, but feedback during the
- * preview window is especially valuable in shaping the final product. During the preview period we may make
- * backwards-incompatible changes to the public API, but in general, this is something we will try our best to avoid.
- */
-
#include <aws/mqtt/mqtt.h>
#include <aws/common/array_list.h>
#include <aws/common/byte_buf.h>
+AWS_PUSH_SANE_WARNING_LEVEL
+
/**
* Some artificial (non-MQTT spec specified) limits that we place on input packets (publish, subscribe, unsubscibe)
* which lets us safely do the various packet size calculations with a bare minimum of checked arithmetic.
@@ -482,5 +476,6 @@ struct aws_mqtt5_packet_unsuback_view {
size_t reason_code_count;
const enum aws_mqtt5_unsuback_reason_code *reason_codes;
};
+AWS_POP_SANE_WARNING_LEVEL
#endif /* AWS_MQTT_MQTT5_TYPES_H */
diff --git a/contrib/restricted/aws/aws-c-mqtt/source/client.c b/contrib/restricted/aws/aws-c-mqtt/source/client.c
index 7aea13b727f..c332c4da864 100644
--- a/contrib/restricted/aws/aws-c-mqtt/source/client.c
+++ b/contrib/restricted/aws/aws-c-mqtt/source/client.c
@@ -11,6 +11,8 @@
#include <aws/mqtt/private/topic_tree.h>
#include <aws/http/proxy.h>
+#include <aws/http/request_response.h>
+#include <aws/http/websocket.h>
#include <aws/io/channel_bootstrap.h>
#include <aws/io/event_loop.h>
@@ -23,13 +25,9 @@
#include <inttypes.h>
-#ifdef AWS_MQTT_WITH_WEBSOCKETS
-# include <aws/http/request_response.h>
-# include <aws/http/websocket.h>
-#endif
-
#ifdef _MSC_VER
# pragma warning(disable : 4204)
+# pragma warning(disable : 4996) /* allow strncpy() */
#endif
/* 3 seconds */
@@ -38,21 +36,23 @@ static const uint64_t s_default_ping_timeout_ns = 3000000000;
/* 20 minutes - This is the default (and max) for AWS IoT as of 2020.02.18 */
static const uint16_t s_default_keep_alive_sec = 1200;
+#define DEFAULT_MQTT311_OPERATION_TABLE_SIZE 100
+
static int s_mqtt_client_connect(
- struct aws_mqtt_client_connection *connection,
+ struct aws_mqtt_client_connection_311_impl *connection,
aws_mqtt_client_on_connection_complete_fn *on_connection_complete,
void *userdata);
/*******************************************************************************
* Helper functions
******************************************************************************/
-void mqtt_connection_lock_synced_data(struct aws_mqtt_client_connection *connection) {
+void mqtt_connection_lock_synced_data(struct aws_mqtt_client_connection_311_impl *connection) {
int err = aws_mutex_lock(&connection->synced_data.lock);
AWS_ASSERT(!err);
(void)err;
}
-void mqtt_connection_unlock_synced_data(struct aws_mqtt_client_connection *connection) {
+void mqtt_connection_unlock_synced_data(struct aws_mqtt_client_connection_311_impl *connection) {
ASSERT_SYNCED_DATA_LOCK_HELD(connection);
int err = aws_mutex_unlock(&connection->synced_data.lock);
@@ -60,7 +60,7 @@ void mqtt_connection_unlock_synced_data(struct aws_mqtt_client_connection *conne
(void)err;
}
-static void s_aws_mqtt_schedule_reconnect_task(struct aws_mqtt_client_connection *connection) {
+static void s_aws_mqtt_schedule_reconnect_task(struct aws_mqtt_client_connection_311_impl *connection) {
uint64_t next_attempt_ns = 0;
aws_high_res_clock_get_ticks(&next_attempt_ns);
next_attempt_ns += aws_timestamp_convert(
@@ -83,7 +83,7 @@ static void s_aws_mqtt_client_destroy(struct aws_mqtt_client *client) {
}
void mqtt_connection_set_state(
- struct aws_mqtt_client_connection *connection,
+ struct aws_mqtt_client_connection_311_impl *connection,
enum aws_mqtt_client_connection_state state) {
ASSERT_SYNCED_DATA_LOCK_HELD(connection);
if (connection->synced_data.state == state) {
@@ -98,7 +98,7 @@ struct request_timeout_wrapper;
/* used for timeout task */
struct request_timeout_task_arg {
uint16_t packet_id;
- struct aws_mqtt_client_connection *connection;
+ struct aws_mqtt_client_connection_311_impl *connection;
struct request_timeout_wrapper *task_arg_wrapper;
};
@@ -115,7 +115,7 @@ struct request_timeout_wrapper {
static void s_request_timeout(struct aws_channel_task *channel_task, void *arg, enum aws_task_status status) {
(void)channel_task;
struct request_timeout_task_arg *timeout_task_arg = arg;
- struct aws_mqtt_client_connection *connection = timeout_task_arg->connection;
+ struct aws_mqtt_client_connection_311_impl *connection = timeout_task_arg->connection;
if (status == AWS_TASK_STATUS_RUN_READY) {
if (timeout_task_arg->task_arg_wrapper != NULL) {
@@ -138,7 +138,7 @@ static void s_request_timeout(struct aws_channel_task *channel_task, void *arg,
}
static struct request_timeout_task_arg *s_schedule_timeout_task(
- struct aws_mqtt_client_connection *connection,
+ struct aws_mqtt_client_connection_311_impl *connection,
uint16_t packet_id) {
/* schedule a timeout task to run, in case server consider the publish is not received */
struct aws_channel_task *request_timeout_task = NULL;
@@ -173,6 +173,44 @@ static void s_init_statistics(struct aws_mqtt_connection_operation_statistics_im
aws_atomic_store_int(&stats->unacked_operation_size_atomic, 0);
}
+static bool s_is_topic_shared_topic(struct aws_byte_cursor *input) {
+ char *input_str = (char *)input->ptr;
+ if (strncmp("$share/", input_str, strlen("$share/")) == 0) {
+ return true;
+ }
+ return false;
+}
+
+static struct aws_string *s_get_normal_topic_from_shared_topic(struct aws_string *input) {
+ const char *input_char_str = aws_string_c_str(input);
+ size_t input_char_length = strlen(input_char_str);
+ size_t split_position = 7; // Start at '$share/' since we know it has to exist
+ while (split_position < input_char_length) {
+ split_position += 1;
+ if (input_char_str[split_position] == '/') {
+ break;
+ }
+ }
+ // If we got all the way to the end, OR there is not at least a single character
+ // after the second /, then it's invalid input.
+ if (split_position + 1 >= input_char_length) {
+ AWS_LOGF_ERROR(AWS_LS_MQTT_CLIENT, "Cannot parse shared subscription topic: Topic is not formatted correctly");
+ return NULL;
+ }
+ const size_t split_delta = input_char_length - split_position;
+ if (split_delta > 0) {
+ // Annoyingly, we cannot just use 'char result_char[split_delta];' because
+ // MSVC doesn't support it.
+ char *result_char = aws_mem_calloc(input->allocator, split_delta, sizeof(char));
+ strncpy(result_char, input_char_str + split_position + 1, split_delta);
+ struct aws_string *result_string = aws_string_new_from_c_str(input->allocator, (const char *)result_char);
+ aws_mem_release(input->allocator, result_char);
+ return result_string;
+ }
+ AWS_LOGF_ERROR(AWS_LS_MQTT_CLIENT, "Cannot parse shared subscription topic: Topic is not formatted correctly");
+ return NULL;
+}
+
/*******************************************************************************
* Client Init
******************************************************************************/
@@ -220,7 +258,7 @@ static void s_mqtt_client_shutdown(
(void)bootstrap;
(void)channel;
- struct aws_mqtt_client_connection *connection = user_data;
+ struct aws_mqtt_client_connection_311_impl *connection = user_data;
AWS_LOGF_TRACE(
AWS_LS_MQTT_CLIENT, "id=%p: Channel has been shutdown with error code %d", (void *)connection, error_code);
@@ -327,7 +365,7 @@ static void s_mqtt_client_shutdown(
struct aws_mqtt_request *request = AWS_CONTAINER_OF(current, struct aws_mqtt_request, list_node);
if (request->on_complete) {
request->on_complete(
- connection,
+ &connection->base,
request->packet_id,
AWS_ERROR_MQTT_CANCELLED_FOR_CLEAN_SESSION,
request->on_complete_ud);
@@ -419,12 +457,13 @@ static void s_mqtt_client_shutdown(
"id=%p: Initial connection attempt failed, calling callback",
(void *)connection);
MQTT_CLIENT_CALL_CALLBACK_ARGS(connection, on_connection_complete, error_code, 0, false);
+ MQTT_CLIENT_CALL_CALLBACK_ARGS(connection, on_connection_failure, error_code);
break;
default:
break;
}
/* The connection can die now. Release the refcount */
- aws_mqtt_client_connection_release(connection);
+ aws_mqtt_client_connection_release(&connection->base);
}
}
@@ -436,7 +475,7 @@ static void s_mqtt_client_shutdown(
* for a CONNACK, kill it off. In the case that the connection died between scheduling this task and it being executed
* the status will always be CANCELED because this task will be canceled when the owning channel goes away. */
static void s_connack_received_timeout(struct aws_channel_task *channel_task, void *arg, enum aws_task_status status) {
- struct aws_mqtt_client_connection *connection = arg;
+ struct aws_mqtt_client_connection_311_impl *connection = arg;
if (status == AWS_TASK_STATUS_RUN_READY) {
bool time_out = false;
@@ -472,7 +511,7 @@ static void s_mqtt_client_init(
/* Setup callback contract is: if error_code is non-zero then channel is NULL. */
AWS_FATAL_ASSERT((error_code != 0) == (channel == NULL));
- struct aws_mqtt_client_connection *connection = user_data;
+ struct aws_mqtt_client_connection_311_impl *connection = user_data;
if (error_code != AWS_OP_SUCCESS) {
/* client shutdown already handles this case, so just call that. */
@@ -503,7 +542,7 @@ static void s_mqtt_client_init(
mqtt_connection_unlock_synced_data(connection);
} /* END CRITICAL SECTION */
- /* intall the slot and handler */
+ /* install the slot and handler */
if (failed_create_slot) {
AWS_LOGF_ERROR(
@@ -538,6 +577,8 @@ static void s_mqtt_client_init(
goto handle_error;
}
+ aws_mqtt311_decoder_reset_for_new_connection(&connection->thread_data.decoder);
+
AWS_LOGF_DEBUG(
AWS_LS_MQTT_CLIENT, "id=%p: Connection successfully opened, sending CONNECT packet", (void *)connection);
@@ -635,6 +676,7 @@ static void s_mqtt_client_init(
handle_error:
MQTT_CLIENT_CALL_CALLBACK_ARGS(connection, on_connection_complete, aws_last_error(), 0, false);
+ MQTT_CLIENT_CALL_CALLBACK_ARGS(connection, on_connection_failure, aws_last_error());
aws_channel_shutdown(channel, aws_last_error());
if (message) {
@@ -647,13 +689,57 @@ static void s_attempt_reconnect(struct aws_task *task, void *userdata, enum aws_
(void)task;
struct aws_mqtt_reconnect_task *reconnect = userdata;
- struct aws_mqtt_client_connection *connection = aws_atomic_load_ptr(&reconnect->connection_ptr);
+ struct aws_mqtt_client_connection_311_impl *connection = aws_atomic_load_ptr(&reconnect->connection_ptr);
+ /* If the task is not cancelled and a connection has not succeeded, attempt reconnect */
if (status == AWS_TASK_STATUS_RUN_READY && connection) {
- /* If the task is not cancelled and a connection has not succeeded, attempt reconnect */
-
mqtt_connection_lock_synced_data(connection);
+ /**
+ * Check the state and if we are disconnecting (AWS_MQTT_CLIENT_STATE_DISCONNECTING) then we want to skip it
+ * and abort the reconnect task (or rather, just do not try to reconnect)
+ */
+ if (connection->synced_data.state == AWS_MQTT_CLIENT_STATE_DISCONNECTING) {
+ AWS_LOGF_TRACE(
+ AWS_LS_MQTT_CLIENT, "id=%p: Skipping reconnect: Client is trying to disconnect", (void *)connection);
+
+ /**
+ * There is the nasty world where the disconnect task/function is called right when we are "reconnecting" as
+ * our state but we have not reconnected. When this happens, the disconnect function doesn't do anything
+ * beyond setting the state to AWS_MQTT_CLIENT_STATE_DISCONNECTING (aws_mqtt_client_connection_disconnect),
+ * meaning the disconnect callback will NOT be called nor will we release memory.
+ * For this reason, we have to do the callback and release of the connection here otherwise the code
+ * will DEADLOCK forever and that is bad.
+ */
+ bool perform_full_destroy = false;
+ if (!connection->slot) {
+ AWS_LOGF_TRACE(
+ AWS_LS_MQTT_CLIENT,
+ "id=%p: Reconnect task called but client is disconnecting and has no slot. Finishing disconnect",
+ (void *)connection);
+ mqtt_connection_set_state(connection, AWS_MQTT_CLIENT_STATE_DISCONNECTED);
+ perform_full_destroy = true;
+ }
+
+ aws_mem_release(reconnect->allocator, reconnect);
+ connection->reconnect_task = NULL;
+
+ /* Unlock the synced data, then potentially call the disconnect callback and release the connection */
+ mqtt_connection_unlock_synced_data(connection);
+ if (perform_full_destroy) {
+ MQTT_CLIENT_CALL_CALLBACK(connection, on_disconnect);
+ MQTT_CLIENT_CALL_CALLBACK_ARGS(connection, on_closed, NULL);
+ aws_mqtt_client_connection_release(&connection->base);
+ }
+ return;
+ }
+
+ AWS_LOGF_TRACE(
+ AWS_LS_MQTT_CLIENT,
+ "id=%p: Attempting reconnect, if it fails next attempt will be in %" PRIu64 " seconds",
+ (void *)connection,
+ connection->reconnect_timeouts.current_sec);
+
/* Check before multiplying to avoid potential overflow */
if (connection->reconnect_timeouts.current_sec > connection->reconnect_timeouts.max_sec / 2) {
connection->reconnect_timeouts.current_sec = connection->reconnect_timeouts.max_sec;
@@ -682,7 +768,7 @@ static void s_attempt_reconnect(struct aws_task *task, void *userdata, enum aws_
}
}
-void aws_create_reconnect_task(struct aws_mqtt_client_connection *connection) {
+void aws_create_reconnect_task(struct aws_mqtt_client_connection_311_impl *connection) {
if (connection->reconnect_task == NULL) {
connection->reconnect_task = aws_mem_calloc(connection->allocator, 1, sizeof(struct aws_mqtt_reconnect_task));
AWS_FATAL_ASSERT(connection->reconnect_task != NULL);
@@ -694,15 +780,9 @@ void aws_create_reconnect_task(struct aws_mqtt_client_connection *connection) {
}
}
-static uint64_t s_hash_uint16_t(const void *item) {
- return *(uint16_t *)item;
-}
+static void s_mqtt_client_connection_destroy_final(struct aws_mqtt_client_connection *base_connection) {
-static bool s_uint16_t_eq(const void *a, const void *b) {
- return *(uint16_t *)a == *(uint16_t *)b;
-}
-
-static void s_mqtt_client_connection_destroy_final(struct aws_mqtt_client_connection *connection) {
+ struct aws_mqtt_client_connection_311_impl *connection = base_connection->impl;
AWS_PRECONDITION(!connection || connection->allocator);
if (!connection) {
return;
@@ -714,6 +794,13 @@ static void s_mqtt_client_connection_destroy_final(struct aws_mqtt_client_connec
AWS_LOGF_DEBUG(AWS_LS_MQTT_CLIENT, "id=%p: Destroying connection", (void *)connection);
+ aws_mqtt_client_on_connection_termination_fn *termination_handler = NULL;
+ void *termination_handler_user_data = NULL;
+ if (connection->on_termination != NULL) {
+ termination_handler = connection->on_termination;
+ termination_handler_user_data = connection->on_termination_ud;
+ }
+
/* If the reconnect_task isn't freed, free it */
if (connection->reconnect_task) {
aws_mem_release(connection->reconnect_task->allocator, connection->reconnect_task);
@@ -738,6 +825,8 @@ static void s_mqtt_client_connection_destroy_final(struct aws_mqtt_client_connec
/* Free all of the active subscriptions */
aws_mqtt_topic_tree_clean_up(&connection->thread_data.subscriptions);
+ aws_mqtt311_decoder_clean_up(&connection->thread_data.decoder);
+
aws_hash_table_clean_up(&connection->synced_data.outstanding_requests_table);
/* clean up the pending_requests if it's not empty */
while (!aws_linked_list_empty(&connection->synced_data.pending_requests_list)) {
@@ -746,7 +835,7 @@ static void s_mqtt_client_connection_destroy_final(struct aws_mqtt_client_connec
/* Fire the callback and clean up the memory, as the connection get destroyed. */
if (request->on_complete) {
request->on_complete(
- connection, request->packet_id, AWS_ERROR_MQTT_CONNECTION_DESTROYED, request->on_complete_ud);
+ &connection->base, request->packet_id, AWS_ERROR_MQTT_CONNECTION_DESTROYED, request->on_complete_ud);
}
aws_memory_pool_release(&connection->synced_data.requests_pool, request);
}
@@ -766,6 +855,10 @@ static void s_mqtt_client_connection_destroy_final(struct aws_mqtt_client_connec
/* Frees all allocated memory */
aws_mem_release(connection->allocator, connection);
+
+ if (termination_handler != NULL) {
+ (*termination_handler)(termination_handler_user_data);
+ }
}
static void s_on_final_disconnect(struct aws_mqtt_client_connection *connection, void *userdata) {
@@ -774,7 +867,7 @@ static void s_on_final_disconnect(struct aws_mqtt_client_connection *connection,
s_mqtt_client_connection_destroy_final(connection);
}
-static void s_mqtt_client_connection_start_destroy(struct aws_mqtt_client_connection *connection) {
+static void s_mqtt_client_connection_start_destroy(struct aws_mqtt_client_connection_311_impl *connection) {
bool call_destroy_final = false;
AWS_LOGF_DEBUG(
@@ -806,121 +899,7 @@ static void s_mqtt_client_connection_start_destroy(struct aws_mqtt_client_connec
} /* END CRITICAL SECTION */
if (call_destroy_final) {
- s_mqtt_client_connection_destroy_final(connection);
- }
-}
-
-struct aws_mqtt_client_connection *aws_mqtt_client_connection_new(struct aws_mqtt_client *client) {
- AWS_PRECONDITION(client);
-
- struct aws_mqtt_client_connection *connection =
- aws_mem_calloc(client->allocator, 1, sizeof(struct aws_mqtt_client_connection));
- if (!connection) {
- return NULL;
- }
-
- AWS_LOGF_DEBUG(AWS_LS_MQTT_CLIENT, "id=%p: Creating new connection", (void *)connection);
-
- /* Initialize the client */
- connection->allocator = client->allocator;
- aws_ref_count_init(
- &connection->ref_count, connection, (aws_simple_completion_callback *)s_mqtt_client_connection_start_destroy);
- connection->client = aws_mqtt_client_acquire(client);
- AWS_ZERO_STRUCT(connection->synced_data);
- connection->synced_data.state = AWS_MQTT_CLIENT_STATE_DISCONNECTED;
- connection->reconnect_timeouts.min_sec = 1;
- connection->reconnect_timeouts.current_sec = 1;
- connection->reconnect_timeouts.max_sec = 128;
- aws_linked_list_init(&connection->synced_data.pending_requests_list);
- aws_linked_list_init(&connection->thread_data.ongoing_requests_list);
- s_init_statistics(&connection->operation_statistics_impl);
-
- if (aws_mutex_init(&connection->synced_data.lock)) {
- AWS_LOGF_ERROR(
- AWS_LS_MQTT_CLIENT,
- "id=%p: Failed to initialize mutex, error %d (%s)",
- (void *)connection,
- aws_last_error(),
- aws_error_name(aws_last_error()));
- goto failed_init_mutex;
- }
-
- if (aws_mqtt_topic_tree_init(&connection->thread_data.subscriptions, connection->allocator)) {
-
- AWS_LOGF_ERROR(
- AWS_LS_MQTT_CLIENT,
- "id=%p: Failed to initialize subscriptions topic_tree, error %d (%s)",
- (void *)connection,
- aws_last_error(),
- aws_error_name(aws_last_error()));
- goto failed_init_subscriptions;
- }
-
- if (aws_memory_pool_init(
- &connection->synced_data.requests_pool, connection->allocator, 32, sizeof(struct aws_mqtt_request))) {
-
- AWS_LOGF_ERROR(
- AWS_LS_MQTT_CLIENT,
- "id=%p: Failed to initialize request pool, error %d (%s)",
- (void *)connection,
- aws_last_error(),
- aws_error_name(aws_last_error()));
- goto failed_init_requests_pool;
- }
-
- if (aws_hash_table_init(
- &connection->synced_data.outstanding_requests_table,
- connection->allocator,
- sizeof(struct aws_mqtt_request *),
- s_hash_uint16_t,
- s_uint16_t_eq,
- NULL,
- NULL)) {
-
- AWS_LOGF_ERROR(
- AWS_LS_MQTT_CLIENT,
- "id=%p: Failed to initialize outstanding requests table, error %d (%s)",
- (void *)connection,
- aws_last_error(),
- aws_error_name(aws_last_error()));
- goto failed_init_outstanding_requests_table;
- }
-
- connection->loop = aws_event_loop_group_get_next_loop(client->bootstrap->event_loop_group);
-
- /* Initialize the handler */
- connection->handler.alloc = connection->allocator;
- connection->handler.vtable = aws_mqtt_get_client_channel_vtable();
- connection->handler.impl = connection;
-
- return connection;
-
-failed_init_outstanding_requests_table:
- aws_memory_pool_clean_up(&connection->synced_data.requests_pool);
-
-failed_init_requests_pool:
- aws_mqtt_topic_tree_clean_up(&connection->thread_data.subscriptions);
-
-failed_init_subscriptions:
- aws_mutex_clean_up(&connection->synced_data.lock);
-
-failed_init_mutex:
- aws_mem_release(client->allocator, connection);
-
- return NULL;
-}
-
-struct aws_mqtt_client_connection *aws_mqtt_client_connection_acquire(struct aws_mqtt_client_connection *connection) {
- if (connection != NULL) {
- aws_ref_count_acquire(&connection->ref_count);
- }
-
- return connection;
-}
-
-void aws_mqtt_client_connection_release(struct aws_mqtt_client_connection *connection) {
- if (connection != NULL) {
- aws_ref_count_release(&connection->ref_count);
+ s_mqtt_client_connection_destroy_final(&connection->base);
}
}
@@ -929,7 +908,7 @@ void aws_mqtt_client_connection_release(struct aws_mqtt_client_connection *conne
******************************************************************************/
/* To configure the connection, ensure the state is DISCONNECTED or CONNECTED */
-static int s_check_connection_state_for_configuration(struct aws_mqtt_client_connection *connection) {
+static int s_check_connection_state_for_configuration(struct aws_mqtt_client_connection_311_impl *connection) {
int result = AWS_OP_SUCCESS;
{ /* BEGIN CRITICAL SECTION */
mqtt_connection_lock_synced_data(connection);
@@ -948,19 +927,31 @@ static int s_check_connection_state_for_configuration(struct aws_mqtt_client_con
return result;
}
-int aws_mqtt_client_connection_set_will(
- struct aws_mqtt_client_connection *connection,
+static int s_aws_mqtt_client_connection_311_set_will(
+ void *impl,
const struct aws_byte_cursor *topic,
enum aws_mqtt_qos qos,
bool retain,
const struct aws_byte_cursor *payload) {
+ struct aws_mqtt_client_connection_311_impl *connection = impl;
+
AWS_PRECONDITION(connection);
AWS_PRECONDITION(topic);
if (s_check_connection_state_for_configuration(connection)) {
return aws_raise_error(AWS_ERROR_INVALID_STATE);
}
+ if (!aws_mqtt_is_valid_topic(topic)) {
+ AWS_LOGF_ERROR(AWS_LS_MQTT_CLIENT, "id=%p: Will topic is invalid", (void *)connection);
+ return aws_raise_error(AWS_ERROR_MQTT_INVALID_TOPIC);
+ }
+
+ if (qos > AWS_MQTT_QOS_EXACTLY_ONCE) {
+ AWS_LOGF_ERROR(AWS_LS_MQTT_CLIENT, "id=%p: Will qos is invalid", (void *)connection);
+ return aws_raise_error(AWS_ERROR_MQTT_INVALID_QOS);
+ }
+
int result = AWS_OP_ERR;
AWS_LOGF_TRACE(
AWS_LS_MQTT_CLIENT,
@@ -968,11 +959,6 @@ int aws_mqtt_client_connection_set_will(
(void *)connection,
AWS_BYTE_CURSOR_PRI(*topic));
- if (!aws_mqtt_is_valid_topic(topic)) {
- AWS_LOGF_ERROR(AWS_LS_MQTT_CLIENT, "id=%p: Will topic is invalid", (void *)connection);
- return aws_raise_error(AWS_ERROR_MQTT_INVALID_TOPIC);
- }
-
struct aws_byte_buf local_topic_buf;
struct aws_byte_buf local_payload_buf;
AWS_ZERO_STRUCT(local_topic_buf);
@@ -1013,17 +999,25 @@ cleanup:
return result;
}
-int aws_mqtt_client_connection_set_login(
- struct aws_mqtt_client_connection *connection,
+static int s_aws_mqtt_client_connection_311_set_login(
+ void *impl,
const struct aws_byte_cursor *username,
const struct aws_byte_cursor *password) {
+ struct aws_mqtt_client_connection_311_impl *connection = impl;
+
AWS_PRECONDITION(connection);
AWS_PRECONDITION(username);
if (s_check_connection_state_for_configuration(connection)) {
return aws_raise_error(AWS_ERROR_INVALID_STATE);
}
+ if (username != NULL && aws_mqtt_validate_utf8_text(*username) == AWS_OP_ERR) {
+ AWS_LOGF_DEBUG(
+ AWS_LS_MQTT_CLIENT, "id=%p: Invalid utf8 or forbidden codepoints in username", (void *)connection);
+ return aws_raise_error(AWS_ERROR_INVALID_ARGUMENT);
+ }
+
int result = AWS_OP_ERR;
AWS_LOGF_TRACE(AWS_LS_MQTT_CLIENT, "id=%p: Setting username and password", (void *)connection);
@@ -1066,11 +1060,13 @@ cleanup:
return result;
}
-int aws_mqtt_client_connection_set_reconnect_timeout(
- struct aws_mqtt_client_connection *connection,
+static int s_aws_mqtt_client_connection_311_set_reconnect_timeout(
+ void *impl,
uint64_t min_timeout,
uint64_t max_timeout) {
+ struct aws_mqtt_client_connection_311_impl *connection = impl;
+
AWS_PRECONDITION(connection);
if (s_check_connection_state_for_configuration(connection)) {
return aws_raise_error(AWS_ERROR_INVALID_STATE);
@@ -1088,13 +1084,38 @@ int aws_mqtt_client_connection_set_reconnect_timeout(
return AWS_OP_SUCCESS;
}
-int aws_mqtt_client_connection_set_connection_interruption_handlers(
- struct aws_mqtt_client_connection *connection,
+static int s_aws_mqtt_client_connection_311_set_connection_result_handlers(
+ void *impl,
+ aws_mqtt_client_on_connection_success_fn *on_connection_success,
+ void *on_connection_success_ud,
+ aws_mqtt_client_on_connection_failure_fn *on_connection_failure,
+ void *on_connection_failure_ud) {
+
+ struct aws_mqtt_client_connection_311_impl *connection = impl;
+
+ AWS_PRECONDITION(connection);
+ if (s_check_connection_state_for_configuration(connection)) {
+ return aws_raise_error(AWS_ERROR_INVALID_STATE);
+ }
+ AWS_LOGF_TRACE(AWS_LS_MQTT_CLIENT, "id=%p: Setting connection success and failure handlers", (void *)connection);
+
+ connection->on_connection_success = on_connection_success;
+ connection->on_connection_success_ud = on_connection_success_ud;
+ connection->on_connection_failure = on_connection_failure;
+ connection->on_connection_failure_ud = on_connection_failure_ud;
+
+ return AWS_OP_SUCCESS;
+}
+
+static int s_aws_mqtt_client_connection_311_set_connection_interruption_handlers(
+ void *impl,
aws_mqtt_client_on_connection_interrupted_fn *on_interrupted,
void *on_interrupted_ud,
aws_mqtt_client_on_connection_resumed_fn *on_resumed,
void *on_resumed_ud) {
+ struct aws_mqtt_client_connection_311_impl *connection = impl;
+
AWS_PRECONDITION(connection);
if (s_check_connection_state_for_configuration(connection)) {
return aws_raise_error(AWS_ERROR_INVALID_STATE);
@@ -1110,11 +1131,13 @@ int aws_mqtt_client_connection_set_connection_interruption_handlers(
return AWS_OP_SUCCESS;
}
-int aws_mqtt_client_connection_set_connection_closed_handler(
- struct aws_mqtt_client_connection *connection,
+static int s_aws_mqtt_client_connection_311_set_connection_closed_handler(
+ void *impl,
aws_mqtt_client_on_connection_closed_fn *on_closed,
void *on_closed_ud) {
+ struct aws_mqtt_client_connection_311_impl *connection = impl;
+
AWS_PRECONDITION(connection);
if (s_check_connection_state_for_configuration(connection)) {
return aws_raise_error(AWS_ERROR_INVALID_STATE);
@@ -1127,11 +1150,13 @@ int aws_mqtt_client_connection_set_connection_closed_handler(
return AWS_OP_SUCCESS;
}
-int aws_mqtt_client_connection_set_on_any_publish_handler(
- struct aws_mqtt_client_connection *connection,
+static int s_aws_mqtt_client_connection_311_set_on_any_publish_handler(
+ void *impl,
aws_mqtt_client_publish_received_fn *on_any_publish,
void *on_any_publish_ud) {
+ struct aws_mqtt_client_connection_311_impl *connection = impl;
+
AWS_PRECONDITION(connection);
{ /* BEGIN CRITICAL SECTION */
mqtt_connection_lock_synced_data(connection);
@@ -1156,18 +1181,38 @@ int aws_mqtt_client_connection_set_on_any_publish_handler(
return AWS_OP_SUCCESS;
}
+static int s_aws_mqtt_client_connection_311_set_connection_termination_handler(
+ void *impl,
+ aws_mqtt_client_on_connection_termination_fn *on_termination,
+ void *on_termination_ud) {
+
+ struct aws_mqtt_client_connection_311_impl *connection = impl;
+
+ AWS_PRECONDITION(connection);
+ if (s_check_connection_state_for_configuration(connection)) {
+ return aws_raise_error(AWS_ERROR_INVALID_STATE);
+ }
+ AWS_LOGF_TRACE(AWS_LS_MQTT_CLIENT, "id=%p: Setting connection termination handler", (void *)connection);
+
+ connection->on_termination = on_termination;
+ connection->on_termination_ud = on_termination_ud;
+
+ return AWS_OP_SUCCESS;
+}
+
/*******************************************************************************
* Websockets
******************************************************************************/
-#ifdef AWS_MQTT_WITH_WEBSOCKETS
-int aws_mqtt_client_connection_use_websockets(
- struct aws_mqtt_client_connection *connection,
+static int s_aws_mqtt_client_connection_311_use_websockets(
+ void *impl,
aws_mqtt_transform_websocket_handshake_fn *transformer,
void *transformer_ud,
aws_mqtt_validate_websocket_handshake_fn *validator,
void *validator_ud) {
+ struct aws_mqtt_client_connection_311_impl *connection = impl;
+
connection->websocket.handshake_transformer = transformer;
connection->websocket.handshake_transformer_ud = transformer_ud;
connection->websocket.handshake_validator = validator;
@@ -1179,10 +1224,12 @@ int aws_mqtt_client_connection_use_websockets(
return AWS_OP_SUCCESS;
}
-int aws_mqtt_client_connection_set_http_proxy_options(
- struct aws_mqtt_client_connection *connection,
+static int s_aws_mqtt_client_connection_311_set_http_proxy_options(
+ void *impl,
struct aws_http_proxy_options *proxy_options) {
+ struct aws_mqtt_client_connection_311_impl *connection = impl;
+
/* If there is existing proxy options, nuke em */
if (connection->http_proxy_config) {
aws_http_proxy_config_destroy(connection->http_proxy_config);
@@ -1195,8 +1242,19 @@ int aws_mqtt_client_connection_set_http_proxy_options(
return connection->http_proxy_config != NULL ? AWS_OP_SUCCESS : AWS_OP_ERR;
}
+static int s_aws_mqtt_client_connection_311_set_host_resolution_options(
+ void *impl,
+ const struct aws_host_resolution_config *host_resolution_config) {
+
+ struct aws_mqtt_client_connection_311_impl *connection = impl;
+
+ connection->host_resolution_config = *host_resolution_config;
+
+ return AWS_OP_SUCCESS;
+}
+
static void s_on_websocket_shutdown(struct aws_websocket *websocket, int error_code, void *user_data) {
- struct aws_mqtt_client_connection *connection = user_data;
+ struct aws_mqtt_client_connection_311_impl *connection = user_data;
struct aws_channel *channel = connection->slot ? connection->slot->channel : NULL;
@@ -1212,7 +1270,7 @@ static void s_on_websocket_setup(const struct aws_websocket_on_connection_setup_
/* Setup callback contract is: if error_code is non-zero then websocket is NULL. */
AWS_FATAL_ASSERT((setup->error_code != 0) == (setup->websocket == NULL));
- struct aws_mqtt_client_connection *connection = user_data;
+ struct aws_mqtt_client_connection_311_impl *connection = user_data;
struct aws_channel *channel = NULL;
if (connection->websocket.handshake_request) {
@@ -1243,7 +1301,7 @@ static void s_on_websocket_setup(const struct aws_websocket_on_connection_setup_
AWS_LOGF_TRACE(AWS_LS_MQTT_CLIENT, "id=%p: Validating websocket handshake response.", (void *)connection);
if (connection->websocket.handshake_validator(
- connection,
+ &connection->base,
setup->handshake_response_header_array,
setup->num_handshake_response_headers,
connection->websocket.handshake_validator_ud)) {
@@ -1270,7 +1328,7 @@ static void s_on_websocket_setup(const struct aws_websocket_on_connection_setup_
static aws_mqtt_transform_websocket_handshake_complete_fn s_websocket_handshake_transform_complete; /* fwd declare */
-static int s_websocket_connect(struct aws_mqtt_client_connection *connection) {
+static int s_websocket_connect(struct aws_mqtt_client_connection_311_impl *connection) {
AWS_ASSERT(connection->websocket.enabled);
/* Build websocket handshake request */
@@ -1317,7 +1375,7 @@ static void s_websocket_handshake_transform_complete(
int error_code,
void *complete_ctx) {
- struct aws_mqtt_client_connection *connection = complete_ctx;
+ struct aws_mqtt_client_connection_311_impl *connection = complete_ctx;
if (error_code) {
AWS_LOGF_ERROR(
@@ -1346,6 +1404,7 @@ static void s_websocket_handshake_transform_complete(
.on_connection_setup = s_on_websocket_setup,
.on_connection_shutdown = s_on_websocket_shutdown,
.requested_event_loop = connection->loop,
+ .host_resolution_config = &connection->host_resolution_config,
};
struct aws_http_proxy_options proxy_options;
@@ -1370,52 +1429,26 @@ error:;
s_on_websocket_setup(&websocket_setup, connection);
}
-#else /* AWS_MQTT_WITH_WEBSOCKETS */
-int aws_mqtt_client_connection_use_websockets(
- struct aws_mqtt_client_connection *connection,
- aws_mqtt_transform_websocket_handshake_fn *transformer,
- void *transformer_ud,
- aws_mqtt_validate_websocket_handshake_fn *validator,
- void *validator_ud) {
-
- (void)connection;
- (void)transformer;
- (void)transformer_ud;
- (void)validator;
- (void)validator_ud;
-
- AWS_LOGF_ERROR(
- AWS_LS_MQTT_CLIENT,
- "id=%p: Cannot use websockets unless library is built with MQTT_WITH_WEBSOCKETS option.",
- (void *)connection);
-
- return aws_raise_error(AWS_ERROR_MQTT_BUILT_WITHOUT_WEBSOCKETS);
-}
-
-int aws_mqtt_client_connection_set_websocket_proxy_options(
- struct aws_mqtt_client_connection *connection,
- struct aws_http_proxy_options *proxy_options) {
-
- (void)connection;
- (void)proxy_options;
-
- AWS_LOGF_ERROR(
- AWS_LS_MQTT_CLIENT,
- "id=%p: Cannot use websockets unless library is built with MQTT_WITH_WEBSOCKETS option.",
- (void *)connection);
-
- return aws_raise_error(AWS_ERROR_MQTT_BUILT_WITHOUT_WEBSOCKETS);
-}
-#endif /* AWS_MQTT_WITH_WEBSOCKETS */
-
/*******************************************************************************
* Connect
******************************************************************************/
-int aws_mqtt_client_connection_connect(
- struct aws_mqtt_client_connection *connection,
+static int s_aws_mqtt_client_connection_311_connect(
+ void *impl,
const struct aws_mqtt_connection_options *connection_options) {
+ struct aws_mqtt_client_connection_311_impl *connection = impl;
+
+ if (connection_options == NULL) {
+ return aws_raise_error(AWS_ERROR_INVALID_ARGUMENT);
+ }
+
+ if (aws_mqtt_validate_utf8_text(connection_options->client_id) == AWS_OP_ERR) {
+ AWS_LOGF_DEBUG(
+ AWS_LS_MQTT_CLIENT, "id=%p: Invalid utf8 or forbidden codepoints in client id", (void *)connection);
+ return aws_raise_error(AWS_ERROR_INVALID_ARGUMENT);
+ }
+
/* TODO: Do we need to support resuming the connection if user connect to the same connection & endpoint and the
* clean_session is false?
* If not, the broker will resume the connection in this case, and we pretend we are making a new connection, which
@@ -1455,6 +1488,9 @@ int aws_mqtt_client_connection_connect(
if (!connection->keep_alive_time_secs) {
connection->keep_alive_time_secs = s_default_keep_alive_sec;
}
+ connection->keep_alive_time_ns =
+ aws_timestamp_convert(connection->keep_alive_time_secs, AWS_TIMESTAMP_SECS, AWS_TIMESTAMP_NANOS, NULL);
+
if (!connection_options->protocol_operation_timeout_ms) {
connection->operation_timeout_ns = UINT64_MAX;
} else {
@@ -1473,16 +1509,15 @@ int aws_mqtt_client_connection_connect(
}
/* Keep alive time should always be greater than the timeouts. */
- if (AWS_UNLIKELY(connection->keep_alive_time_secs * (uint64_t)AWS_TIMESTAMP_NANOS <= connection->ping_timeout_ns)) {
+ if (AWS_UNLIKELY(connection->keep_alive_time_ns <= connection->ping_timeout_ns)) {
AWS_LOGF_FATAL(
AWS_LS_MQTT_CLIENT,
"id=%p: Illegal configuration, Connection keep alive %" PRIu64
"ns must be greater than the request timeouts %" PRIu64 "ns.",
(void *)connection,
- (uint64_t)connection->keep_alive_time_secs * (uint64_t)AWS_TIMESTAMP_NANOS,
+ connection->keep_alive_time_ns,
connection->ping_timeout_ns);
- AWS_FATAL_ASSERT(
- connection->keep_alive_time_secs * (uint64_t)AWS_TIMESTAMP_NANOS > connection->ping_timeout_ns);
+ AWS_FATAL_ASSERT(connection->keep_alive_time_ns > connection->ping_timeout_ns);
}
AWS_LOGF_INFO(
@@ -1557,7 +1592,7 @@ int aws_mqtt_client_connection_connect(
request->packet_id);
if (request->on_complete) {
request->on_complete(
- connection,
+ &connection->base,
request->packet_id,
AWS_ERROR_MQTT_CANCELLED_FOR_CLEAN_SESSION,
request->on_complete_ud);
@@ -1579,14 +1614,14 @@ int aws_mqtt_client_connection_connect(
}
/* Begin the connecting process, acquire the connection to keep it alive until we disconnected */
- aws_mqtt_client_connection_acquire(connection);
+ aws_mqtt_client_connection_acquire(&connection->base);
if (s_mqtt_client_connect(connection, connection_options->on_connection_complete, connection_options->user_data)) {
/*
* An error calling s_mqtt_client_connect should (must) be mutually exclusive with s_mqtt_client_shutdown().
* So it should be safe and correct to call release now to undo the pinning we did a few lines above.
*/
- aws_mqtt_client_connection_release(connection);
+ aws_mqtt_client_connection_release(&connection->base);
/* client_id has been updated with something but it will get cleaned up when the connection gets cleaned up
* so we don't need to worry about it here*/
@@ -1611,19 +1646,16 @@ error:
}
static int s_mqtt_client_connect(
- struct aws_mqtt_client_connection *connection,
+ struct aws_mqtt_client_connection_311_impl *connection,
aws_mqtt_client_on_connection_complete_fn *on_connection_complete,
void *userdata) {
connection->on_connection_complete = on_connection_complete;
connection->on_connection_complete_ud = userdata;
int result = 0;
-#ifdef AWS_MQTT_WITH_WEBSOCKETS
if (connection->websocket.enabled) {
result = s_websocket_connect(connection);
- } else
-#endif /* AWS_MQTT_WITH_WEBSOCKETS */
- {
+ } else {
struct aws_socket_channel_bootstrap_options channel_options;
AWS_ZERO_STRUCT(channel_options);
channel_options.bootstrap = connection->client->bootstrap;
@@ -1635,6 +1667,7 @@ static int s_mqtt_client_connect(
channel_options.shutdown_callback = &s_mqtt_client_shutdown;
channel_options.user_data = connection;
channel_options.requested_event_loop = connection->loop;
+ channel_options.host_resolution_override_config = &connection->host_resolution_config;
if (connection->http_proxy_config == NULL) {
result = aws_client_bootstrap_new_socket_channel(&channel_options);
@@ -1665,11 +1698,11 @@ static int s_mqtt_client_connect(
* Reconnect DEPRECATED
******************************************************************************/
-int aws_mqtt_client_connection_reconnect(
- struct aws_mqtt_client_connection *connection,
+static int s_aws_mqtt_client_connection_311_reconnect(
+ void *impl,
aws_mqtt_client_on_connection_complete_fn *on_connection_complete,
void *userdata) {
- (void)connection;
+ (void)impl;
(void)on_connection_complete;
(void)userdata;
@@ -1682,11 +1715,13 @@ int aws_mqtt_client_connection_reconnect(
* Disconnect
******************************************************************************/
-int aws_mqtt_client_connection_disconnect(
- struct aws_mqtt_client_connection *connection,
+static int s_aws_mqtt_client_connection_311_disconnect(
+ void *impl,
aws_mqtt_client_on_disconnect_fn *on_disconnect,
void *userdata) {
+ struct aws_mqtt_client_connection_311_impl *connection = impl;
+
AWS_LOGF_DEBUG(AWS_LS_MQTT_CLIENT, "id=%p: user called disconnect.", (void *)connection);
{ /* BEGIN CRITICAL SECTION */
@@ -1734,7 +1769,7 @@ static void s_on_publish_client_wrapper(
/* Call out to the user callback */
if (task_topic->request.on_publish) {
task_topic->request.on_publish(
- task_topic->connection, topic, payload, dup, qos, retain, task_topic->request.on_publish_ud);
+ &task_topic->connection->base, topic, payload, dup, qos, retain, task_topic->request.on_publish_ud);
}
}
@@ -1801,16 +1836,41 @@ static enum aws_mqtt_client_request_state s_subscribe_send(uint16_t packet_id, b
}
if (!task_arg->tree_updated) {
- if (aws_mqtt_topic_tree_transaction_insert(
- &task_arg->connection->thread_data.subscriptions,
- &transaction,
- topic->filter,
- topic->request.qos,
- s_on_publish_client_wrapper,
- s_task_topic_release,
- topic)) {
- goto handle_error;
+ struct aws_byte_cursor filter_cursor = aws_byte_cursor_from_string(topic->filter);
+ if (s_is_topic_shared_topic(&filter_cursor)) {
+ struct aws_string *normal_topic = s_get_normal_topic_from_shared_topic(topic->filter);
+ if (normal_topic == NULL) {
+ AWS_LOGF_ERROR(
+ AWS_LS_MQTT_CLIENT,
+ "id=%p: Topic is shared subscription topic but topic could not be parsed from "
+ "shared subscription topic.",
+ (void *)task_arg->connection);
+ goto handle_error;
+ }
+ if (aws_mqtt_topic_tree_transaction_insert(
+ &task_arg->connection->thread_data.subscriptions,
+ &transaction,
+ normal_topic,
+ topic->request.qos,
+ s_on_publish_client_wrapper,
+ s_task_topic_release,
+ topic)) {
+ aws_string_destroy(normal_topic);
+ goto handle_error;
+ }
+ aws_string_destroy(normal_topic);
+ } else {
+ if (aws_mqtt_topic_tree_transaction_insert(
+ &task_arg->connection->thread_data.subscriptions,
+ &transaction,
+ topic->filter,
+ topic->request.qos,
+ s_on_publish_client_wrapper,
+ s_task_topic_release,
+ topic)) {
+ goto handle_error;
+ }
}
/* If insert succeed, acquire the refcount */
aws_ref_count_acquire(&topic->ref_count);
@@ -1856,11 +1916,12 @@ handle_error:
}
static void s_subscribe_complete(
- struct aws_mqtt_client_connection *connection,
+ struct aws_mqtt_client_connection *connection_base,
uint16_t packet_id,
int error_code,
void *userdata) {
+ struct aws_mqtt_client_connection_311_impl *connection = connection_base->impl;
struct subscribe_task_arg *task_arg = userdata;
struct subscribe_task_topic *topic = NULL;
@@ -1887,11 +1948,16 @@ static void s_subscribe_complete(
err |= aws_array_list_push_back(&cb_list, &subscription);
}
AWS_ASSUME(!err);
- task_arg->on_suback.multi(connection, packet_id, &cb_list, error_code, task_arg->on_suback_ud);
+ task_arg->on_suback.multi(&connection->base, packet_id, &cb_list, error_code, task_arg->on_suback_ud);
aws_array_list_clean_up(&cb_list);
} else if (task_arg->on_suback.single) {
task_arg->on_suback.single(
- connection, packet_id, &topic->request.topic, topic->request.qos, error_code, task_arg->on_suback_ud);
+ &connection->base,
+ packet_id,
+ &topic->request.topic,
+ topic->request.qos,
+ error_code,
+ task_arg->on_suback_ud);
}
for (size_t i = 0; i < list_len; i++) {
aws_array_list_get_at(&task_arg->topics, &topic, i);
@@ -1902,14 +1968,21 @@ static void s_subscribe_complete(
aws_mem_release(task_arg->connection->allocator, task_arg);
}
-uint16_t aws_mqtt_client_connection_subscribe_multiple(
- struct aws_mqtt_client_connection *connection,
+static uint16_t s_aws_mqtt_client_connection_311_subscribe_multiple(
+ void *impl,
const struct aws_array_list *topic_filters,
aws_mqtt_suback_multi_fn *on_suback,
void *on_suback_ud) {
+ struct aws_mqtt_client_connection_311_impl *connection = impl;
+
AWS_PRECONDITION(connection);
+ if (topic_filters == NULL || aws_array_list_length(topic_filters) == 0) {
+ aws_raise_error(AWS_ERROR_INVALID_ARGUMENT);
+ return 0;
+ }
+
struct subscribe_task_arg *task_arg = aws_mem_calloc(connection->allocator, 1, sizeof(struct subscribe_task_arg));
if (!task_arg) {
return 0;
@@ -2026,11 +2099,12 @@ handle_error:
******************************************************************************/
static void s_subscribe_single_complete(
- struct aws_mqtt_client_connection *connection,
+ struct aws_mqtt_client_connection *connection_base,
uint16_t packet_id,
int error_code,
void *userdata) {
+ struct aws_mqtt_client_connection_311_impl *connection = connection_base->impl;
struct subscribe_task_arg *task_arg = userdata;
AWS_LOGF_DEBUG(
@@ -2047,7 +2121,13 @@ static void s_subscribe_single_complete(
if (task_arg->on_suback.single) {
AWS_ASSUME(aws_string_is_valid(topic->filter));
aws_mqtt_suback_fn *suback = task_arg->on_suback.single;
- suback(connection, packet_id, &topic->request.topic, topic->request.qos, error_code, task_arg->on_suback_ud);
+ suback(
+ &connection->base,
+ packet_id,
+ &topic->request.topic,
+ topic->request.qos,
+ error_code,
+ task_arg->on_suback_ud);
}
s_task_topic_release(topic);
aws_array_list_clean_up(&task_arg->topics);
@@ -2055,8 +2135,8 @@ static void s_subscribe_single_complete(
aws_mem_release(task_arg->connection->allocator, task_arg);
}
-uint16_t aws_mqtt_client_connection_subscribe(
- struct aws_mqtt_client_connection *connection,
+static uint16_t s_aws_mqtt_client_connection_311_subscribe(
+ void *impl,
const struct aws_byte_cursor *topic_filter,
enum aws_mqtt_qos qos,
aws_mqtt_client_publish_received_fn *on_publish,
@@ -2065,6 +2145,8 @@ uint16_t aws_mqtt_client_connection_subscribe(
aws_mqtt_suback_fn *on_suback,
void *on_suback_ud) {
+ struct aws_mqtt_client_connection_311_impl *connection = impl;
+
AWS_PRECONDITION(connection);
if (!aws_mqtt_is_valid_topic_filter(topic_filter)) {
@@ -2168,175 +2250,6 @@ handle_error:
}
/*******************************************************************************
- * Subscribe Local
- ******************************************************************************/
-
-/* The lifetime of this struct is from subscribe -> suback */
-struct subscribe_local_task_arg {
-
- struct aws_mqtt_client_connection *connection;
-
- struct subscribe_task_topic *task_topic;
-
- aws_mqtt_suback_fn *on_suback;
- void *on_suback_ud;
-};
-
-static enum aws_mqtt_client_request_state s_subscribe_local_send(
- uint16_t packet_id,
- bool is_first_attempt,
- void *userdata) {
-
- (void)is_first_attempt;
-
- struct subscribe_local_task_arg *task_arg = userdata;
-
- AWS_LOGF_TRACE(
- AWS_LS_MQTT_CLIENT,
- "id=%p: Attempting save of local subscribe %" PRIu16 " (%s)",
- (void *)task_arg->connection,
- packet_id,
- is_first_attempt ? "first attempt" : "redo");
-
- struct subscribe_task_topic *topic = task_arg->task_topic;
- if (aws_mqtt_topic_tree_insert(
- &task_arg->connection->thread_data.subscriptions,
- topic->filter,
- topic->request.qos,
- s_on_publish_client_wrapper,
- s_task_topic_release,
- topic)) {
-
- return AWS_MQTT_CLIENT_REQUEST_ERROR;
- }
- aws_ref_count_acquire(&topic->ref_count);
-
- return AWS_MQTT_CLIENT_REQUEST_COMPLETE;
-}
-
-static void s_subscribe_local_complete(
- struct aws_mqtt_client_connection *connection,
- uint16_t packet_id,
- int error_code,
- void *userdata) {
-
- struct subscribe_local_task_arg *task_arg = userdata;
-
- AWS_LOGF_DEBUG(
- AWS_LS_MQTT_CLIENT,
- "id=%p: Local subscribe %" PRIu16 " completed with error code %d",
- (void *)connection,
- packet_id,
- error_code);
-
- struct subscribe_task_topic *topic = task_arg->task_topic;
- if (task_arg->on_suback) {
- aws_mqtt_suback_fn *suback = task_arg->on_suback;
- suback(connection, packet_id, &topic->request.topic, topic->request.qos, error_code, task_arg->on_suback_ud);
- }
- s_task_topic_release(topic);
-
- aws_mem_release(task_arg->connection->allocator, task_arg);
-}
-
-uint16_t aws_mqtt_client_connection_subscribe_local(
- struct aws_mqtt_client_connection *connection,
- const struct aws_byte_cursor *topic_filter,
- aws_mqtt_client_publish_received_fn *on_publish,
- void *on_publish_ud,
- aws_mqtt_userdata_cleanup_fn *on_ud_cleanup,
- aws_mqtt_suback_fn *on_suback,
- void *on_suback_ud) {
-
- AWS_PRECONDITION(connection);
-
- if (!aws_mqtt_is_valid_topic_filter(topic_filter)) {
- aws_raise_error(AWS_ERROR_MQTT_INVALID_TOPIC);
- return 0;
- }
-
- struct subscribe_task_topic *task_topic = NULL;
-
- struct subscribe_local_task_arg *task_arg =
- aws_mem_calloc(connection->allocator, 1, sizeof(struct subscribe_local_task_arg));
-
- if (!task_arg) {
- goto handle_error;
- }
- AWS_ZERO_STRUCT(*task_arg);
-
- task_arg->connection = connection;
- task_arg->on_suback = on_suback;
- task_arg->on_suback_ud = on_suback_ud;
- task_topic = aws_mem_calloc(connection->allocator, 1, sizeof(struct subscribe_task_topic));
- if (!task_topic) {
- goto handle_error;
- }
- aws_ref_count_init(&task_topic->ref_count, task_topic, (aws_simple_completion_callback *)s_task_topic_clean_up);
- task_arg->task_topic = task_topic;
-
- task_topic->filter = aws_string_new_from_array(connection->allocator, topic_filter->ptr, topic_filter->len);
- if (!task_topic->filter) {
- goto handle_error;
- }
-
- task_topic->connection = connection;
- task_topic->is_local = true;
- task_topic->request.topic = aws_byte_cursor_from_string(task_topic->filter);
- task_topic->request.on_publish = on_publish;
- task_topic->request.on_cleanup = on_ud_cleanup;
- task_topic->request.on_publish_ud = on_publish_ud;
-
- /* Calculate the size of the (local) subscribe packet
- * The fixed header is 2 bytes, the packet ID is 2 bytes
- * the topic filter is always 3 bytes (1 for QoS, 2 for Length MSB/LSB)
- * - plus the size of the topic filter */
- uint64_t subscribe_packet_size = 7 + topic_filter->len;
-
- uint16_t packet_id = mqtt_create_request(
- task_arg->connection,
- s_subscribe_local_send,
- task_arg,
- &s_subscribe_local_complete,
- task_arg,
- false, /* noRetry */
- subscribe_packet_size);
-
- if (packet_id == 0) {
- AWS_LOGF_ERROR(
- AWS_LS_MQTT_CLIENT,
- "id=%p: Failed to start local subscribe on topic " PRInSTR " with error %s",
- (void *)connection,
- AWS_BYTE_CURSOR_PRI(task_topic->request.topic),
- aws_error_debug_str(aws_last_error()));
- goto handle_error;
- }
-
- AWS_LOGF_DEBUG(
- AWS_LS_MQTT_CLIENT,
- "id=%p: Starting local subscribe %" PRIu16 " on topic " PRInSTR,
- (void *)connection,
- packet_id,
- AWS_BYTE_CURSOR_PRI(task_topic->request.topic));
- return packet_id;
-
-handle_error:
-
- if (task_topic) {
- if (task_topic->filter) {
- aws_string_destroy(task_topic->filter);
- }
- aws_mem_release(connection->allocator, task_topic);
- }
-
- if (task_arg) {
- aws_mem_release(connection->allocator, task_arg);
- }
-
- return 0;
-}
-
-/*******************************************************************************
* Resubscribe
******************************************************************************/
@@ -2452,11 +2365,13 @@ handle_error:
}
static void s_resubscribe_complete(
- struct aws_mqtt_client_connection *connection,
+ struct aws_mqtt_client_connection *connection_base,
uint16_t packet_id,
int error_code,
void *userdata) {
+ struct aws_mqtt_client_connection_311_impl *connection = connection_base->impl;
+
struct subscribe_task_arg *task_arg = userdata;
const size_t list_len = aws_array_list_length(&task_arg->topics);
@@ -2487,11 +2402,16 @@ static void s_resubscribe_complete(
err |= aws_array_list_push_back(&cb_list, &subscription);
}
AWS_ASSUME(!err);
- task_arg->on_suback.multi(connection, packet_id, &cb_list, error_code, task_arg->on_suback_ud);
+ task_arg->on_suback.multi(&connection->base, packet_id, &cb_list, error_code, task_arg->on_suback_ud);
aws_array_list_clean_up(&cb_list);
} else if (task_arg->on_suback.single) {
task_arg->on_suback.single(
- connection, packet_id, &topic->request.topic, topic->request.qos, error_code, task_arg->on_suback_ud);
+ &connection->base,
+ packet_id,
+ &topic->request.topic,
+ topic->request.qos,
+ error_code,
+ task_arg->on_suback_ud);
}
clean_up:
@@ -2507,11 +2427,13 @@ clean_up:
aws_mem_release(task_arg->connection->allocator, task_arg);
}
-uint16_t aws_mqtt_resubscribe_existing_topics(
- struct aws_mqtt_client_connection *connection,
+static uint16_t s_aws_mqtt_311_resubscribe_existing_topics(
+ void *impl,
aws_mqtt_suback_multi_fn *on_suback,
void *on_suback_ud) {
+ struct aws_mqtt_client_connection_311_impl *connection = impl;
+
struct subscribe_task_arg *task_arg = aws_mem_calloc(connection->allocator, 1, sizeof(struct subscribe_task_arg));
if (!task_arg) {
AWS_LOGF_ERROR(
@@ -2569,10 +2491,10 @@ handle_error:
******************************************************************************/
struct unsubscribe_task_arg {
- struct aws_mqtt_client_connection *connection;
+ struct aws_mqtt_client_connection_311_impl *connection;
struct aws_string *filter_string;
struct aws_byte_cursor filter;
- bool is_local;
+
/* Packet to populate */
struct aws_mqtt_packet_unsubscribe unsubscribe;
@@ -2611,62 +2533,89 @@ static enum aws_mqtt_client_request_state s_unsubscribe_send(
if (!task_arg->tree_updated) {
struct subscribe_task_topic *topic;
- if (aws_mqtt_topic_tree_transaction_remove(
- &task_arg->connection->thread_data.subscriptions, &transaction, &task_arg->filter, (void **)&topic)) {
- goto handle_error;
- }
- task_arg->is_local = topic ? topic->is_local : false;
- }
-
- if (!task_arg->is_local) {
- if (task_arg->unsubscribe.fixed_header.packet_type == 0) {
- /* If unsubscribe packet is uninitialized, init it */
- if (aws_mqtt_packet_unsubscribe_init(&task_arg->unsubscribe, task_arg->connection->allocator, packet_id)) {
+ if (s_is_topic_shared_topic(&task_arg->filter)) {
+ struct aws_string *shared_topic =
+ aws_string_new_from_cursor(task_arg->connection->allocator, &task_arg->filter);
+ struct aws_string *normal_topic = s_get_normal_topic_from_shared_topic(shared_topic);
+ if (normal_topic == NULL) {
+ AWS_LOGF_ERROR(
+ AWS_LS_MQTT_CLIENT,
+ "id=%p: Topic is shared subscription topic but topic could not be parsed from "
+ "shared subscription topic.",
+ (void *)task_arg->connection);
+ aws_string_destroy(shared_topic);
goto handle_error;
}
- if (aws_mqtt_packet_unsubscribe_add_topic(&task_arg->unsubscribe, task_arg->filter)) {
+ struct aws_byte_cursor normal_topic_cursor = aws_byte_cursor_from_string(normal_topic);
+ if (aws_mqtt_topic_tree_transaction_remove(
+ &task_arg->connection->thread_data.subscriptions,
+ &transaction,
+ &normal_topic_cursor,
+ (void **)&topic)) {
+ aws_string_destroy(shared_topic);
+ aws_string_destroy(normal_topic);
+ goto handle_error;
+ }
+ aws_string_destroy(shared_topic);
+ aws_string_destroy(normal_topic);
+ } else {
+ if (aws_mqtt_topic_tree_transaction_remove(
+ &task_arg->connection->thread_data.subscriptions,
+ &transaction,
+ &task_arg->filter,
+ (void **)&topic)) {
goto handle_error;
}
}
+ }
- message = mqtt_get_message_for_packet(task_arg->connection, &task_arg->unsubscribe.fixed_header);
- if (!message) {
+ if (task_arg->unsubscribe.fixed_header.packet_type == 0) {
+ /* If unsubscribe packet is uninitialized, init it */
+ if (aws_mqtt_packet_unsubscribe_init(&task_arg->unsubscribe, task_arg->connection->allocator, packet_id)) {
goto handle_error;
}
-
- if (aws_mqtt_packet_unsubscribe_encode(&message->message_data, &task_arg->unsubscribe)) {
+ if (aws_mqtt_packet_unsubscribe_add_topic(&task_arg->unsubscribe, task_arg->filter)) {
goto handle_error;
}
+ }
- if (aws_channel_slot_send_message(task_arg->connection->slot, message, AWS_CHANNEL_DIR_WRITE)) {
- goto handle_error;
- }
+ message = mqtt_get_message_for_packet(task_arg->connection, &task_arg->unsubscribe.fixed_header);
+ if (!message) {
+ goto handle_error;
+ }
- /* TODO: timing should start from the message written into the socket, which is aws_io_message->on_completion
- * invoked, but there are bugs in the websocket handler (and maybe also the h1 handler?) where we don't properly
- * fire the on_completion callbacks. */
- struct request_timeout_task_arg *timeout_task_arg = s_schedule_timeout_task(task_arg->connection, packet_id);
- if (!timeout_task_arg) {
- return AWS_MQTT_CLIENT_REQUEST_ERROR;
- }
+ if (aws_mqtt_packet_unsubscribe_encode(&message->message_data, &task_arg->unsubscribe)) {
+ goto handle_error;
+ }
- /*
- * Set up mutual references between the operation task args and the timeout task args. Whoever runs first
- * "wins", does its logic, and then breaks the connection between the two.
- */
- task_arg->timeout_wrapper.timeout_task_arg = timeout_task_arg;
- timeout_task_arg->task_arg_wrapper = &task_arg->timeout_wrapper;
+ if (aws_channel_slot_send_message(task_arg->connection->slot, message, AWS_CHANNEL_DIR_WRITE)) {
+ goto handle_error;
+ }
+
+ /* TODO: timing should start from the message written into the socket, which is aws_io_message->on_completion
+ * invoked, but there are bugs in the websocket handler (and maybe also the h1 handler?) where we don't properly
+ * fire the on_completion callbacks. */
+ struct request_timeout_task_arg *timeout_task_arg = s_schedule_timeout_task(task_arg->connection, packet_id);
+ if (!timeout_task_arg) {
+ return AWS_MQTT_CLIENT_REQUEST_ERROR;
}
+ /*
+ * Set up mutual references between the operation task args and the timeout task args. Whoever runs first
+ * "wins", does its logic, and then breaks the connection between the two.
+ */
+ task_arg->timeout_wrapper.timeout_task_arg = timeout_task_arg;
+ timeout_task_arg->task_arg_wrapper = &task_arg->timeout_wrapper;
+
if (!task_arg->tree_updated) {
aws_mqtt_topic_tree_transaction_commit(&task_arg->connection->thread_data.subscriptions, &transaction);
task_arg->tree_updated = true;
}
aws_array_list_clean_up(&transaction);
- /* If the subscribe is local-only, don't wait for a SUBACK to come back. */
- return task_arg->is_local ? AWS_MQTT_CLIENT_REQUEST_COMPLETE : AWS_MQTT_CLIENT_REQUEST_ONGOING;
+
+ return AWS_MQTT_CLIENT_REQUEST_ONGOING;
handle_error:
@@ -2682,11 +2631,13 @@ handle_error:
}
static void s_unsubscribe_complete(
- struct aws_mqtt_client_connection *connection,
+ struct aws_mqtt_client_connection *connection_base,
uint16_t packet_id,
int error_code,
void *userdata) {
+ struct aws_mqtt_client_connection_311_impl *connection = connection_base->impl;
+
struct unsubscribe_task_arg *task_arg = userdata;
AWS_LOGF_DEBUG(AWS_LS_MQTT_CLIENT, "id=%p: Unsubscribe %" PRIu16 " complete", (void *)connection, packet_id);
@@ -2703,7 +2654,7 @@ static void s_unsubscribe_complete(
}
if (task_arg->on_unsuback) {
- task_arg->on_unsuback(connection, packet_id, error_code, task_arg->on_unsuback_ud);
+ task_arg->on_unsuback(&connection->base, packet_id, error_code, task_arg->on_unsuback_ud);
}
aws_string_destroy(task_arg->filter_string);
@@ -2711,12 +2662,14 @@ static void s_unsubscribe_complete(
aws_mem_release(task_arg->connection->allocator, task_arg);
}
-uint16_t aws_mqtt_client_connection_unsubscribe(
- struct aws_mqtt_client_connection *connection,
+static uint16_t s_aws_mqtt_client_connection_311_unsubscribe(
+ void *impl,
const struct aws_byte_cursor *topic_filter,
aws_mqtt_op_complete_fn *on_unsuback,
void *on_unsuback_ud) {
+ struct aws_mqtt_client_connection_311_impl *connection = impl;
+
AWS_PRECONDITION(connection);
if (!aws_mqtt_is_valid_topic_filter(topic_filter)) {
@@ -2775,7 +2728,7 @@ handle_error:
******************************************************************************/
struct publish_task_arg {
- struct aws_mqtt_client_connection *connection;
+ struct aws_mqtt_client_connection_311_impl *connection;
struct aws_string *topic_string;
struct aws_byte_cursor topic;
enum aws_mqtt_qos qos;
@@ -2794,7 +2747,7 @@ struct publish_task_arg {
/* should only be called by tests */
static int s_get_stuff_from_outstanding_requests_table(
- struct aws_mqtt_client_connection *connection,
+ struct aws_mqtt_client_connection_311_impl *connection,
uint16_t packet_id,
struct aws_allocator *allocator,
struct aws_byte_buf *result_buf,
@@ -2829,29 +2782,29 @@ static int s_get_stuff_from_outstanding_requests_table(
/* should only be called by tests */
int aws_mqtt_client_get_payload_for_outstanding_publish_packet(
- struct aws_mqtt_client_connection *connection,
+ struct aws_mqtt_client_connection *connection_base,
uint16_t packet_id,
struct aws_allocator *allocator,
struct aws_byte_buf *result) {
AWS_ZERO_STRUCT(*result);
- return s_get_stuff_from_outstanding_requests_table(connection, packet_id, allocator, result, NULL);
+ return s_get_stuff_from_outstanding_requests_table(connection_base->impl, packet_id, allocator, result, NULL);
}
/* should only be called by tests */
int aws_mqtt_client_get_topic_for_outstanding_publish_packet(
- struct aws_mqtt_client_connection *connection,
+ struct aws_mqtt_client_connection *connection_base,
uint16_t packet_id,
struct aws_allocator *allocator,
struct aws_string **result) {
*result = NULL;
- return s_get_stuff_from_outstanding_requests_table(connection, packet_id, allocator, NULL, result);
+ return s_get_stuff_from_outstanding_requests_table(connection_base->impl, packet_id, allocator, NULL, result);
}
static enum aws_mqtt_client_request_state s_publish_send(uint16_t packet_id, bool is_first_attempt, void *userdata) {
struct publish_task_arg *task_arg = userdata;
- struct aws_mqtt_client_connection *connection = task_arg->connection;
+ struct aws_mqtt_client_connection_311_impl *connection = task_arg->connection;
AWS_LOGF_TRACE(
AWS_LS_MQTT_CLIENT,
@@ -2877,6 +2830,8 @@ static enum aws_mqtt_client_request_state s_publish_send(uint16_t packet_id, boo
return AWS_MQTT_CLIENT_REQUEST_ERROR;
}
+ } else {
+ aws_mqtt_packet_publish_set_dup(&task_arg->publish);
}
struct aws_io_message *message = mqtt_get_message_for_packet(task_arg->connection, &task_arg->publish.fixed_header);
@@ -2943,16 +2898,19 @@ static enum aws_mqtt_client_request_state s_publish_send(uint16_t packet_id, boo
}
static void s_publish_complete(
- struct aws_mqtt_client_connection *connection,
+ struct aws_mqtt_client_connection *connection_base,
uint16_t packet_id,
int error_code,
void *userdata) {
+
+ struct aws_mqtt_client_connection_311_impl *connection = connection_base->impl;
+
struct publish_task_arg *task_arg = userdata;
AWS_LOGF_DEBUG(AWS_LS_MQTT_CLIENT, "id=%p: Publish %" PRIu16 " complete", (void *)connection, packet_id);
if (task_arg->on_complete) {
- task_arg->on_complete(connection, packet_id, error_code, task_arg->userdata);
+ task_arg->on_complete(&connection->base, packet_id, error_code, task_arg->userdata);
}
/*
@@ -2971,8 +2929,8 @@ static void s_publish_complete(
aws_mem_release(connection->allocator, task_arg);
}
-uint16_t aws_mqtt_client_connection_publish(
- struct aws_mqtt_client_connection *connection,
+static uint16_t s_aws_mqtt_client_connection_311_publish(
+ void *impl,
const struct aws_byte_cursor *topic,
enum aws_mqtt_qos qos,
bool retain,
@@ -2980,6 +2938,8 @@ uint16_t aws_mqtt_client_connection_publish(
aws_mqtt_op_complete_fn *on_complete,
void *userdata) {
+ struct aws_mqtt_client_connection_311_impl *connection = impl;
+
AWS_PRECONDITION(connection);
if (!aws_mqtt_is_valid_topic(topic)) {
@@ -2987,6 +2947,11 @@ uint16_t aws_mqtt_client_connection_publish(
return 0;
}
+ if (qos > AWS_MQTT_QOS_EXACTLY_ONCE) {
+ aws_raise_error(AWS_ERROR_MQTT_INVALID_QOS);
+ return 0;
+ }
+
struct publish_task_arg *arg = aws_mem_calloc(connection->allocator, 1, sizeof(struct publish_task_arg));
if (!arg) {
return 0;
@@ -2997,7 +2962,14 @@ uint16_t aws_mqtt_client_connection_publish(
arg->topic = aws_byte_cursor_from_string(arg->topic_string);
arg->qos = qos;
arg->retain = retain;
- if (aws_byte_buf_init_copy_from_cursor(&arg->payload_buf, connection->allocator, *payload)) {
+
+ struct aws_byte_cursor payload_cursor;
+ AWS_ZERO_STRUCT(payload_cursor);
+ if (payload != NULL) {
+ payload_cursor = *payload;
+ }
+
+ if (aws_byte_buf_init_copy_from_cursor(&arg->payload_buf, connection->allocator, payload_cursor)) {
goto handle_error;
}
arg->payload = aws_byte_cursor_from_buf(&arg->payload_buf);
@@ -3052,7 +3024,7 @@ handle_error:
******************************************************************************/
static void s_pingresp_received_timeout(struct aws_channel_task *channel_task, void *arg, enum aws_task_status status) {
- struct aws_mqtt_client_connection *connection = arg;
+ struct aws_mqtt_client_connection_311_impl *connection = arg;
if (status == AWS_TASK_STATUS_RUN_READY) {
/* Check that a pingresp has been received since pingreq was sent */
@@ -3072,7 +3044,7 @@ static enum aws_mqtt_client_request_state s_pingreq_send(uint16_t packet_id, boo
(void)is_first_attempt;
AWS_PRECONDITION(is_first_attempt);
- struct aws_mqtt_client_connection *connection = userdata;
+ struct aws_mqtt_client_connection_311_impl *connection = userdata;
AWS_LOGF_TRACE(AWS_LS_MQTT_CLIENT, "id=%p: pingreq send", (void *)connection);
struct aws_mqtt_packet_connection pingreq;
@@ -3115,7 +3087,7 @@ error:
return AWS_MQTT_CLIENT_REQUEST_ERROR;
}
-int aws_mqtt_client_connection_ping(struct aws_mqtt_client_connection *connection) {
+int aws_mqtt_client_connection_ping(struct aws_mqtt_client_connection_311_impl *connection) {
AWS_LOGF_DEBUG(AWS_LS_MQTT_CLIENT, "id=%p: Starting ping", (void *)connection);
@@ -3132,7 +3104,7 @@ int aws_mqtt_client_connection_ping(struct aws_mqtt_client_connection *connectio
******************************************************************************/
void aws_mqtt_connection_statistics_change_operation_statistic_state(
- struct aws_mqtt_client_connection *connection,
+ struct aws_mqtt_client_connection_311_impl *connection,
struct aws_mqtt_request *request,
enum aws_mqtt_operation_statistic_state_flags new_state_flags) {
@@ -3190,9 +3162,12 @@ void aws_mqtt_connection_statistics_change_operation_statistic_state(
}
}
-int aws_mqtt_client_connection_get_stats(
- struct aws_mqtt_client_connection *connection,
+static int s_aws_mqtt_client_connection_311_get_stats(
+ void *impl,
struct aws_mqtt_connection_operation_statistics *stats) {
+
+ struct aws_mqtt_client_connection_311_impl *connection = impl;
+
// Error checking
if (!connection) {
AWS_LOGF_ERROR(AWS_LS_MQTT_CLIENT, "Invalid MQTT311 connection used when trying to get operation statistics");
@@ -3219,7 +3194,7 @@ int aws_mqtt_client_connection_get_stats(
}
int aws_mqtt_client_connection_set_on_operation_statistics_handler(
- struct aws_mqtt_client_connection *connection,
+ struct aws_mqtt_client_connection_311_impl *connection,
aws_mqtt_on_operation_statistics_fn *on_operation_statistics,
void *on_operation_statistics_ud) {
@@ -3230,3 +3205,158 @@ int aws_mqtt_client_connection_set_on_operation_statistics_handler(
return AWS_OP_SUCCESS;
}
+
+static struct aws_mqtt_client_connection *s_aws_mqtt_client_connection_311_acquire(void *impl) {
+ struct aws_mqtt_client_connection_311_impl *connection = impl;
+
+ aws_ref_count_acquire(&connection->ref_count);
+
+ return &connection->base;
+}
+
+static void s_aws_mqtt_client_connection_311_release(void *impl) {
+ struct aws_mqtt_client_connection_311_impl *connection = impl;
+
+ aws_ref_count_release(&connection->ref_count);
+}
+
+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,
+ .set_will_fn = s_aws_mqtt_client_connection_311_set_will,
+ .set_login_fn = s_aws_mqtt_client_connection_311_set_login,
+ .use_websockets_fn = s_aws_mqtt_client_connection_311_use_websockets,
+ .set_http_proxy_options_fn = s_aws_mqtt_client_connection_311_set_http_proxy_options,
+ .set_host_resolution_options_fn = s_aws_mqtt_client_connection_311_set_host_resolution_options,
+ .set_reconnect_timeout_fn = s_aws_mqtt_client_connection_311_set_reconnect_timeout,
+ .set_connection_result_handlers = s_aws_mqtt_client_connection_311_set_connection_result_handlers,
+ .set_connection_interruption_handlers_fn = s_aws_mqtt_client_connection_311_set_connection_interruption_handlers,
+ .set_connection_closed_handler_fn = s_aws_mqtt_client_connection_311_set_connection_closed_handler,
+ .set_on_any_publish_handler_fn = s_aws_mqtt_client_connection_311_set_on_any_publish_handler,
+ .set_connection_termination_handler_fn = s_aws_mqtt_client_connection_311_set_connection_termination_handler,
+ .connect_fn = s_aws_mqtt_client_connection_311_connect,
+ .reconnect_fn = s_aws_mqtt_client_connection_311_reconnect,
+ .disconnect_fn = s_aws_mqtt_client_connection_311_disconnect,
+ .subscribe_multiple_fn = s_aws_mqtt_client_connection_311_subscribe_multiple,
+ .subscribe_fn = s_aws_mqtt_client_connection_311_subscribe,
+ .resubscribe_existing_topics_fn = s_aws_mqtt_311_resubscribe_existing_topics,
+ .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,
+};
+
+static struct aws_mqtt_client_connection_vtable *s_aws_mqtt_client_connection_311_vtable_ptr =
+ &s_aws_mqtt_client_connection_311_vtable;
+
+struct aws_mqtt_client_connection *aws_mqtt_client_connection_new(struct aws_mqtt_client *client) {
+ AWS_PRECONDITION(client);
+
+ struct aws_mqtt_client_connection_311_impl *connection =
+ aws_mem_calloc(client->allocator, 1, sizeof(struct aws_mqtt_client_connection_311_impl));
+ if (!connection) {
+ return NULL;
+ }
+
+ AWS_LOGF_DEBUG(AWS_LS_MQTT_CLIENT, "id=%p: Creating new mqtt 311 connection", (void *)connection);
+
+ /* Initialize the client */
+ connection->allocator = client->allocator;
+ connection->base.vtable = s_aws_mqtt_client_connection_311_vtable_ptr;
+ connection->base.impl = connection;
+ aws_ref_count_init(
+ &connection->ref_count, connection, (aws_simple_completion_callback *)s_mqtt_client_connection_start_destroy);
+ connection->client = aws_mqtt_client_acquire(client);
+
+ AWS_ZERO_STRUCT(connection->synced_data);
+ connection->synced_data.state = AWS_MQTT_CLIENT_STATE_DISCONNECTED;
+ connection->reconnect_timeouts.min_sec = 1;
+ connection->reconnect_timeouts.current_sec = 1;
+ connection->reconnect_timeouts.max_sec = 128;
+ aws_linked_list_init(&connection->synced_data.pending_requests_list);
+ aws_linked_list_init(&connection->thread_data.ongoing_requests_list);
+ s_init_statistics(&connection->operation_statistics_impl);
+
+ if (aws_mutex_init(&connection->synced_data.lock)) {
+ AWS_LOGF_ERROR(
+ AWS_LS_MQTT_CLIENT,
+ "id=%p: Failed to initialize mutex, error %d (%s)",
+ (void *)connection,
+ aws_last_error(),
+ aws_error_name(aws_last_error()));
+ goto failed_init_mutex;
+ }
+
+ struct aws_mqtt311_decoder_options config = {
+ .packet_handlers = aws_mqtt311_get_default_packet_handlers(),
+ .handler_user_data = connection,
+ };
+ aws_mqtt311_decoder_init(&connection->thread_data.decoder, client->allocator, &config);
+
+ if (aws_mqtt_topic_tree_init(&connection->thread_data.subscriptions, connection->allocator)) {
+
+ AWS_LOGF_ERROR(
+ AWS_LS_MQTT_CLIENT,
+ "id=%p: Failed to initialize subscriptions topic_tree, error %d (%s)",
+ (void *)connection,
+ aws_last_error(),
+ aws_error_name(aws_last_error()));
+ goto failed_init_subscriptions;
+ }
+
+ if (aws_memory_pool_init(
+ &connection->synced_data.requests_pool, connection->allocator, 32, sizeof(struct aws_mqtt_request))) {
+
+ AWS_LOGF_ERROR(
+ AWS_LS_MQTT_CLIENT,
+ "id=%p: Failed to initialize request pool, error %d (%s)",
+ (void *)connection,
+ aws_last_error(),
+ aws_error_name(aws_last_error()));
+ goto failed_init_requests_pool;
+ }
+
+ if (aws_hash_table_init(
+ &connection->synced_data.outstanding_requests_table,
+ connection->allocator,
+ DEFAULT_MQTT311_OPERATION_TABLE_SIZE,
+ aws_mqtt_hash_uint16_t,
+ aws_mqtt_compare_uint16_t_eq,
+ NULL,
+ NULL)) {
+
+ AWS_LOGF_ERROR(
+ AWS_LS_MQTT_CLIENT,
+ "id=%p: Failed to initialize outstanding requests table, error %d (%s)",
+ (void *)connection,
+ aws_last_error(),
+ aws_error_name(aws_last_error()));
+ goto failed_init_outstanding_requests_table;
+ }
+
+ connection->loop = aws_event_loop_group_get_next_loop(client->bootstrap->event_loop_group);
+
+ connection->host_resolution_config = aws_host_resolver_init_default_resolution_config();
+ connection->host_resolution_config.resolve_frequency_ns =
+ aws_timestamp_convert(connection->reconnect_timeouts.max_sec, AWS_TIMESTAMP_SECS, AWS_TIMESTAMP_NANOS, NULL);
+
+ /* Initialize the handler */
+ connection->handler.alloc = connection->allocator;
+ connection->handler.vtable = aws_mqtt_get_client_channel_vtable();
+ connection->handler.impl = connection;
+
+ return &connection->base;
+
+failed_init_outstanding_requests_table:
+ aws_memory_pool_clean_up(&connection->synced_data.requests_pool);
+
+failed_init_requests_pool:
+ aws_mqtt_topic_tree_clean_up(&connection->thread_data.subscriptions);
+
+failed_init_subscriptions:
+ aws_mutex_clean_up(&connection->synced_data.lock);
+
+failed_init_mutex:
+ aws_mem_release(client->allocator, connection);
+
+ return NULL;
+}
diff --git a/contrib/restricted/aws/aws-c-mqtt/source/client_channel_handler.c b/contrib/restricted/aws/aws-c-mqtt/source/client_channel_handler.c
index f9c01cbd454..2719f41c6c3 100644
--- a/contrib/restricted/aws/aws-c-mqtt/source/client_channel_handler.c
+++ b/contrib/restricted/aws/aws-c-mqtt/source/client_channel_handler.c
@@ -21,56 +21,135 @@
#endif
/*******************************************************************************
- * Packet State Machine
+ * Static Helper functions
******************************************************************************/
-typedef int(packet_handler_fn)(struct aws_mqtt_client_connection *connection, struct aws_byte_cursor message_cursor);
+/* Caches the socket write time for ping scheduling purposes */
+static void s_update_next_ping_time(struct aws_mqtt_client_connection_311_impl *connection) {
+ if (connection->slot != NULL && connection->slot->channel != NULL) {
+ aws_channel_current_clock_time(connection->slot->channel, &connection->next_ping_time);
+ aws_add_u64_checked(connection->next_ping_time, connection->keep_alive_time_ns, &connection->next_ping_time);
+ }
+}
-static int s_packet_handler_default(
- struct aws_mqtt_client_connection *connection,
- struct aws_byte_cursor message_cursor) {
- (void)connection;
+/* Caches the request send time. The `request_send_timestamp` will be used to push off ping request on request complete.
+ */
+static void s_update_request_send_time(struct aws_mqtt_request *request) {
+ if (request->connection != NULL && request->connection->slot != NULL &&
+ request->connection->slot->channel != NULL) {
+ aws_channel_current_clock_time(request->connection->slot->channel, &request->request_send_timestamp);
+ }
+}
+
+/* push off next ping time on ack received to last_request_send_timestamp_ns + keep_alive_time_ns
+ * The function must be called in critical section. */
+static void s_pushoff_next_ping_time(
+ struct aws_mqtt_client_connection_311_impl *connection,
+ uint64_t last_request_send_timestamp_ns) {
+ ASSERT_SYNCED_DATA_LOCK_HELD(connection);
+ aws_add_u64_checked(
+ last_request_send_timestamp_ns, connection->keep_alive_time_ns, &last_request_send_timestamp_ns);
+ if (last_request_send_timestamp_ns > connection->next_ping_time) {
+ connection->next_ping_time = last_request_send_timestamp_ns;
+ }
+}
+
+/*******************************************************************************
+ * Packet State Machine
+ ******************************************************************************/
+
+static int s_packet_handler_default(struct aws_byte_cursor message_cursor, void *user_data) {
(void)message_cursor;
+ struct aws_mqtt_client_connection_311_impl *connection = user_data;
AWS_LOGF_ERROR(AWS_LS_MQTT_CLIENT, "id=%p: Unhandled packet type received", (void *)connection);
return aws_raise_error(AWS_ERROR_MQTT_INVALID_PACKET_TYPE);
}
static void s_on_time_to_ping(struct aws_channel_task *channel_task, void *arg, enum aws_task_status status);
-static void s_schedule_ping(struct aws_mqtt_client_connection *connection) {
+static void s_schedule_ping(struct aws_mqtt_client_connection_311_impl *connection) {
aws_channel_task_init(&connection->ping_task, s_on_time_to_ping, connection, "mqtt_ping");
uint64_t now = 0;
aws_channel_current_clock_time(connection->slot->channel, &now);
- AWS_LOGF_TRACE(
- AWS_LS_MQTT_CLIENT, "id=%p: Scheduling PING. current timestamp is %" PRIu64, (void *)connection, now);
- uint64_t schedule_time =
- now + aws_timestamp_convert(connection->keep_alive_time_secs, AWS_TIMESTAMP_SECS, AWS_TIMESTAMP_NANOS, NULL);
+ AWS_LOGF_TRACE(
+ AWS_LS_MQTT_CLIENT, "id=%p: Scheduling PING task. current timestamp is %" PRIu64, (void *)connection, now);
AWS_LOGF_TRACE(
AWS_LS_MQTT_CLIENT,
- "id=%p: The next ping will be run at timestamp %" PRIu64,
+ "id=%p: The next PING task will be run at timestamp %" PRIu64,
(void *)connection,
- schedule_time);
- aws_channel_schedule_task_future(connection->slot->channel, &connection->ping_task, schedule_time);
+ connection->next_ping_time);
+
+ aws_channel_schedule_task_future(connection->slot->channel, &connection->ping_task, connection->next_ping_time);
}
static void s_on_time_to_ping(struct aws_channel_task *channel_task, void *arg, enum aws_task_status status) {
(void)channel_task;
if (status == AWS_TASK_STATUS_RUN_READY) {
- struct aws_mqtt_client_connection *connection = arg;
- AWS_LOGF_TRACE(AWS_LS_MQTT_CLIENT, "id=%p: Sending PING", (void *)connection);
- aws_mqtt_client_connection_ping(connection);
+ struct aws_mqtt_client_connection_311_impl *connection = arg;
+
+ uint64_t now = 0;
+ aws_channel_current_clock_time(connection->slot->channel, &now);
+ if (now >= connection->next_ping_time) {
+ s_update_next_ping_time(connection);
+ AWS_LOGF_TRACE(AWS_LS_MQTT_CLIENT, "id=%p: Sending PING", (void *)connection);
+ aws_mqtt_client_connection_ping(connection);
+ } else {
+
+ AWS_LOGF_TRACE(
+ AWS_LS_MQTT_CLIENT,
+ "id=%p: Skipped sending PING because scheduled ping time %" PRIu64
+ " has not elapsed yet. Current time is %" PRIu64
+ ". Rescheduling ping to run at the scheduled ping time...",
+ (void *)connection,
+ connection->next_ping_time,
+ now);
+ }
s_schedule_ping(connection);
}
}
-static int s_packet_handler_connack(
- struct aws_mqtt_client_connection *connection,
- struct aws_byte_cursor message_cursor) {
- AWS_LOGF_TRACE(AWS_LS_MQTT_CLIENT, "id=%p: CONNACK received", (void *)connection);
+static int s_validate_received_packet_type(
+ struct aws_mqtt_client_connection_311_impl *connection,
+ enum aws_mqtt_packet_type packet_type) {
+ { /* BEGIN CRITICAL SECTION */
+ mqtt_connection_lock_synced_data(connection);
+ /* [MQTT-3.2.0-1] The first packet sent from the Server to the Client MUST be a CONNACK Packet */
+ if (connection->synced_data.state == AWS_MQTT_CLIENT_STATE_CONNECTING &&
+ packet_type != AWS_MQTT_PACKET_CONNACK) {
+ mqtt_connection_unlock_synced_data(connection);
+ AWS_LOGF_ERROR(
+ AWS_LS_MQTT_CLIENT,
+ "id=%p: First message received from the server was not a CONNACK. Terminating connection.",
+ (void *)connection);
+ return aws_raise_error(AWS_ERROR_MQTT_PROTOCOL_ERROR);
+ }
+ mqtt_connection_unlock_synced_data(connection);
+ } /* END CRITICAL SECTION */
+
+ if (AWS_UNLIKELY(packet_type > AWS_MQTT_PACKET_DISCONNECT || packet_type < AWS_MQTT_PACKET_CONNECT)) {
+ AWS_LOGF_ERROR(
+ AWS_LS_MQTT_CLIENT,
+ "id=%p: Invalid packet type received %d. Terminating connection.",
+ (void *)connection,
+ packet_type);
+ return aws_raise_error(AWS_ERROR_MQTT_INVALID_PACKET_TYPE);
+ }
+
+ /* Handle the packet */
+ return AWS_OP_SUCCESS;
+}
+
+static int s_packet_handler_connack(struct aws_byte_cursor message_cursor, void *user_data) {
+
+ struct aws_mqtt_client_connection_311_impl *connection = user_data;
+ AWS_LOGF_DEBUG(AWS_LS_MQTT_CLIENT, "id=%p: CONNACK received", (void *)connection);
+ if (s_validate_received_packet_type(connection, AWS_MQTT_PACKET_CONNACK)) {
+ return AWS_OP_ERR;
+ }
struct aws_mqtt_packet_connack connack;
if (aws_mqtt_packet_connack_decode(&message_cursor, &connack)) {
@@ -173,15 +252,30 @@ static int s_packet_handler_connack(
connection, on_connection_complete, AWS_OP_SUCCESS, connack.connect_return_code, connack.session_present);
}
+ /*
+ * The on_connection_success would get triggered on the successful CONNACK. It invoked with both the first connect
+ * attempt and reconnection attempt as Mqtt5 does not have on_resume callback for reconnection.
+ */
+ AWS_LOGF_TRACE(
+ AWS_LS_MQTT_CLIENT,
+ "id=%p: received a successful CONNACK, invoking on_connection_success callback",
+ (void *)connection);
+ MQTT_CLIENT_CALL_CALLBACK_ARGS(
+ connection, on_connection_success, 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);
s_schedule_ping(connection);
return AWS_OP_SUCCESS;
}
-static int s_packet_handler_publish(
- struct aws_mqtt_client_connection *connection,
- struct aws_byte_cursor message_cursor) {
+static int s_packet_handler_publish(struct aws_byte_cursor message_cursor, void *user_data) {
+ struct aws_mqtt_client_connection_311_impl *connection = user_data;
+ AWS_LOGF_DEBUG(AWS_LS_MQTT_CLIENT, "id=%p: PUBLISH received", (void *)connection);
+ if (s_validate_received_packet_type(connection, AWS_MQTT_PACKET_PUBLISH)) {
+ return AWS_OP_ERR;
+ }
/* TODO: need to handle the QoS 2 message to avoid processing the message a second time */
struct aws_mqtt_packet_publish publish;
@@ -251,7 +345,14 @@ static int s_packet_handler_publish(
return AWS_OP_SUCCESS;
}
-static int s_packet_handler_ack(struct aws_mqtt_client_connection *connection, struct aws_byte_cursor message_cursor) {
+static int s_packet_handler_puback(struct aws_byte_cursor message_cursor, void *user_data) {
+
+ struct aws_mqtt_client_connection_311_impl *connection = user_data;
+ AWS_LOGF_DEBUG(AWS_LS_MQTT_CLIENT, "id=%p: received a PUBACK", (void *)connection);
+ if (s_validate_received_packet_type(connection, AWS_MQTT_PACKET_PUBACK)) {
+ return AWS_OP_ERR;
+ }
+
struct aws_mqtt_packet_ack ack;
if (aws_mqtt_packet_ack_decode(&message_cursor, &ack)) {
return AWS_OP_ERR;
@@ -265,9 +366,14 @@ static int s_packet_handler_ack(struct aws_mqtt_client_connection *connection, s
return AWS_OP_SUCCESS;
}
-static int s_packet_handler_suback(
- struct aws_mqtt_client_connection *connection,
- struct aws_byte_cursor message_cursor) {
+static int s_packet_handler_suback(struct aws_byte_cursor message_cursor, void *user_data) {
+
+ struct aws_mqtt_client_connection_311_impl *connection = user_data;
+ AWS_LOGF_DEBUG(AWS_LS_MQTT_CLIENT, "id=%p: received a SUBACK", (void *)connection);
+ if (s_validate_received_packet_type(connection, AWS_MQTT_PACKET_SUBACK)) {
+ return AWS_OP_ERR;
+ }
+
struct aws_mqtt_packet_suback suback;
if (aws_mqtt_packet_suback_init(&suback, connection->allocator, 0 /* fake packet_id */)) {
return AWS_OP_ERR;
@@ -325,9 +431,33 @@ error:
return AWS_OP_ERR;
}
-static int s_packet_handler_pubrec(
- struct aws_mqtt_client_connection *connection,
- struct aws_byte_cursor message_cursor) {
+static int s_packet_handler_unsuback(struct aws_byte_cursor message_cursor, void *user_data) {
+
+ struct aws_mqtt_client_connection_311_impl *connection = user_data;
+ AWS_LOGF_DEBUG(AWS_LS_MQTT_CLIENT, "id=%p: received a UNSUBACK", (void *)connection);
+ if (s_validate_received_packet_type(connection, AWS_MQTT_PACKET_UNSUBACK)) {
+ return AWS_OP_ERR;
+ }
+
+ struct aws_mqtt_packet_ack ack;
+ if (aws_mqtt_packet_ack_decode(&message_cursor, &ack)) {
+ return AWS_OP_ERR;
+ }
+
+ AWS_LOGF_DEBUG(
+ AWS_LS_MQTT_CLIENT, "id=%p: received ack for message id %" PRIu16, (void *)connection, ack.packet_identifier);
+
+ mqtt_request_complete(connection, AWS_ERROR_SUCCESS, ack.packet_identifier);
+ return AWS_OP_SUCCESS;
+}
+
+static int s_packet_handler_pubrec(struct aws_byte_cursor message_cursor, void *user_data) {
+
+ struct aws_mqtt_client_connection_311_impl *connection = user_data;
+ AWS_LOGF_DEBUG(AWS_LS_MQTT_CLIENT, "id=%p: received a PUBREC", (void *)connection);
+ if (s_validate_received_packet_type(connection, AWS_MQTT_PACKET_PUBREC)) {
+ return AWS_OP_ERR;
+ }
struct aws_mqtt_packet_ack ack;
if (aws_mqtt_packet_ack_decode(&message_cursor, &ack)) {
@@ -363,9 +493,13 @@ on_error:
return AWS_OP_ERR;
}
-static int s_packet_handler_pubrel(
- struct aws_mqtt_client_connection *connection,
- struct aws_byte_cursor message_cursor) {
+static int s_packet_handler_pubrel(struct aws_byte_cursor message_cursor, void *user_data) {
+
+ struct aws_mqtt_client_connection_311_impl *connection = user_data;
+ AWS_LOGF_DEBUG(AWS_LS_MQTT_CLIENT, "id=%p: received a PUBREL", (void *)connection);
+ if (s_validate_received_packet_type(connection, AWS_MQTT_PACKET_PUBREL)) {
+ return AWS_OP_ERR;
+ }
struct aws_mqtt_packet_ack ack;
if (aws_mqtt_packet_ack_decode(&message_cursor, &ack)) {
@@ -398,13 +532,32 @@ on_error:
return AWS_OP_ERR;
}
-static int s_packet_handler_pingresp(
- struct aws_mqtt_client_connection *connection,
- struct aws_byte_cursor message_cursor) {
+static int s_packet_handler_pubcomp(struct aws_byte_cursor message_cursor, void *user_data) {
+
+ struct aws_mqtt_client_connection_311_impl *connection = user_data;
+ AWS_LOGF_DEBUG(AWS_LS_MQTT_CLIENT, "id=%p: received a PUBCOMP", (void *)connection);
+ if (s_validate_received_packet_type(connection, AWS_MQTT_PACKET_PUBCOMP)) {
+ return AWS_OP_ERR;
+ }
+
+ struct aws_mqtt_packet_ack ack;
+ if (aws_mqtt_packet_ack_decode(&message_cursor, &ack)) {
+ return AWS_OP_ERR;
+ }
+
+ AWS_LOGF_DEBUG(
+ AWS_LS_MQTT_CLIENT, "id=%p: received ack for message id %" PRIu16, (void *)connection, ack.packet_identifier);
+
+ mqtt_request_complete(connection, AWS_ERROR_SUCCESS, ack.packet_identifier);
+ return AWS_OP_SUCCESS;
+}
+
+static int s_packet_handler_pingresp(struct aws_byte_cursor message_cursor, void *user_data) {
(void)message_cursor;
- AWS_LOGF_TRACE(AWS_LS_MQTT_CLIENT, "id=%p: PINGRESP received", (void *)connection);
+ struct aws_mqtt_client_connection_311_impl *connection = user_data;
+ AWS_LOGF_DEBUG(AWS_LS_MQTT_CLIENT, "id=%p: PINGRESP received", (void *)connection);
connection->thread_data.waiting_on_ping_response = false;
@@ -412,60 +565,32 @@ static int s_packet_handler_pingresp(
}
/* Bake up a big ol' function table just like Gramma used to make */
-static packet_handler_fn *s_packet_handlers[] = {
- [AWS_MQTT_PACKET_CONNECT] = &s_packet_handler_default,
- [AWS_MQTT_PACKET_CONNACK] = &s_packet_handler_connack,
- [AWS_MQTT_PACKET_PUBLISH] = &s_packet_handler_publish,
- [AWS_MQTT_PACKET_PUBACK] = &s_packet_handler_ack,
- [AWS_MQTT_PACKET_PUBREC] = &s_packet_handler_pubrec,
- [AWS_MQTT_PACKET_PUBREL] = &s_packet_handler_pubrel,
- [AWS_MQTT_PACKET_PUBCOMP] = &s_packet_handler_ack,
- [AWS_MQTT_PACKET_SUBSCRIBE] = &s_packet_handler_default,
- [AWS_MQTT_PACKET_SUBACK] = &s_packet_handler_suback,
- [AWS_MQTT_PACKET_UNSUBSCRIBE] = &s_packet_handler_default,
- [AWS_MQTT_PACKET_UNSUBACK] = &s_packet_handler_ack,
- [AWS_MQTT_PACKET_PINGREQ] = &s_packet_handler_default,
- [AWS_MQTT_PACKET_PINGRESP] = &s_packet_handler_pingresp,
- [AWS_MQTT_PACKET_DISCONNECT] = &s_packet_handler_default,
-};
+static struct aws_mqtt_client_connection_packet_handlers s_default_packet_handlers = {
+ .handlers_by_packet_type = {
+ [AWS_MQTT_PACKET_CONNECT] = &s_packet_handler_default,
+ [AWS_MQTT_PACKET_CONNACK] = &s_packet_handler_connack,
+ [AWS_MQTT_PACKET_PUBLISH] = &s_packet_handler_publish,
+ [AWS_MQTT_PACKET_PUBACK] = &s_packet_handler_puback,
+ [AWS_MQTT_PACKET_PUBREC] = &s_packet_handler_pubrec,
+ [AWS_MQTT_PACKET_PUBREL] = &s_packet_handler_pubrel,
+ [AWS_MQTT_PACKET_PUBCOMP] = &s_packet_handler_pubcomp,
+ [AWS_MQTT_PACKET_SUBSCRIBE] = &s_packet_handler_default,
+ [AWS_MQTT_PACKET_SUBACK] = &s_packet_handler_suback,
+ [AWS_MQTT_PACKET_UNSUBSCRIBE] = &s_packet_handler_default,
+ [AWS_MQTT_PACKET_UNSUBACK] = &s_packet_handler_unsuback,
+ [AWS_MQTT_PACKET_PINGREQ] = &s_packet_handler_default,
+ [AWS_MQTT_PACKET_PINGRESP] = &s_packet_handler_pingresp,
+ [AWS_MQTT_PACKET_DISCONNECT] = &s_packet_handler_default,
+ }};
+
+const struct aws_mqtt_client_connection_packet_handlers *aws_mqtt311_get_default_packet_handlers(void) {
+ return &s_default_packet_handlers;
+}
/*******************************************************************************
* Channel Handler
******************************************************************************/
-static int s_process_mqtt_packet(
- struct aws_mqtt_client_connection *connection,
- enum aws_mqtt_packet_type packet_type,
- struct aws_byte_cursor packet) {
- { /* BEGIN CRITICAL SECTION */
- mqtt_connection_lock_synced_data(connection);
- /* [MQTT-3.2.0-1] The first packet sent from the Server to the Client MUST be a CONNACK Packet */
- if (connection->synced_data.state == AWS_MQTT_CLIENT_STATE_CONNECTING &&
- packet_type != AWS_MQTT_PACKET_CONNACK) {
- mqtt_connection_unlock_synced_data(connection);
- AWS_LOGF_ERROR(
- AWS_LS_MQTT_CLIENT,
- "id=%p: First message received from the server was not a CONNACK. Terminating connection.",
- (void *)connection);
- aws_channel_shutdown(connection->slot->channel, AWS_ERROR_MQTT_PROTOCOL_ERROR);
- return aws_raise_error(AWS_ERROR_MQTT_PROTOCOL_ERROR);
- }
- mqtt_connection_unlock_synced_data(connection);
- } /* END CRITICAL SECTION */
-
- if (AWS_UNLIKELY(packet_type > AWS_MQTT_PACKET_DISCONNECT || packet_type < AWS_MQTT_PACKET_CONNECT)) {
- AWS_LOGF_ERROR(
- AWS_LS_MQTT_CLIENT,
- "id=%p: Invalid packet type received %d. Terminating connection.",
- (void *)connection,
- packet_type);
- return aws_raise_error(AWS_ERROR_MQTT_INVALID_PACKET_TYPE);
- }
-
- /* Handle the packet */
- return s_packet_handlers[packet_type](connection, packet);
-}
-
/**
* Handles incoming messages from the server.
*/
@@ -474,10 +599,10 @@ static int s_process_read_message(
struct aws_channel_slot *slot,
struct aws_io_message *message) {
- struct aws_mqtt_client_connection *connection = handler->impl;
+ struct aws_mqtt_client_connection_311_impl *connection = handler->impl;
if (message->message_type != AWS_IO_MESSAGE_APPLICATION_DATA || message->message_data.len < 1) {
- return AWS_OP_ERR;
+ return aws_raise_error(AWS_ERROR_INVALID_STATE);
}
AWS_LOGF_TRACE(
@@ -489,103 +614,18 @@ static int s_process_read_message(
/* This cursor will be updated as we read through the message. */
struct aws_byte_cursor message_cursor = aws_byte_cursor_from_buf(&message->message_data);
- /* If there's pending packet left over from last time, attempt to complete it. */
- if (connection->thread_data.pending_packet.len) {
- int result = AWS_OP_SUCCESS;
-
- /* This determines how much to read from the message (min(expected_remaining, message.len)) */
- size_t to_read = connection->thread_data.pending_packet.capacity - connection->thread_data.pending_packet.len;
- /* This will be set to false if this message still won't complete the packet object. */
- bool packet_complete = true;
- if (to_read > message_cursor.len) {
- to_read = message_cursor.len;
- packet_complete = false;
- }
-
- /* Write the chunk to the buffer.
- * This will either complete the packet, or be the entirety of message if more data is required. */
- struct aws_byte_cursor chunk = aws_byte_cursor_advance(&message_cursor, to_read);
- AWS_ASSERT(chunk.ptr); /* Guaranteed to be in bounds */
- result = (int)aws_byte_buf_write_from_whole_cursor(&connection->thread_data.pending_packet, chunk) - 1;
- if (result) {
- goto handle_error;
- }
-
- /* If the packet is still incomplete, don't do anything with the data. */
- if (!packet_complete) {
- AWS_LOGF_TRACE(
- AWS_LS_MQTT_CLIENT,
- "id=%p: partial message is still incomplete, waiting on another read.",
- (void *)connection);
-
- goto cleanup;
- }
-
- /* Handle the completed pending packet */
- struct aws_byte_cursor packet_data = aws_byte_cursor_from_buf(&connection->thread_data.pending_packet);
- AWS_LOGF_TRACE(AWS_LS_MQTT_CLIENT, "id=%p: full mqtt packet re-assembled, dispatching.", (void *)connection);
- result = s_process_mqtt_packet(connection, aws_mqtt_get_packet_type(packet_data.ptr), packet_data);
-
- handle_error:
- /* Clean up the pending packet */
- aws_byte_buf_clean_up(&connection->thread_data.pending_packet);
- AWS_ZERO_STRUCT(connection->thread_data.pending_packet);
-
- if (result) {
- return AWS_OP_ERR;
- }
- }
+ int result = aws_mqtt311_decoder_on_bytes_received(&connection->thread_data.decoder, message_cursor);
- while (message_cursor.len) {
-
- /* Temp byte cursor so we can decode the header without advancing message_cursor. */
- struct aws_byte_cursor header_decode = message_cursor;
-
- struct aws_mqtt_fixed_header packet_header;
- AWS_ZERO_STRUCT(packet_header);
- int result = aws_mqtt_fixed_header_decode(&header_decode, &packet_header);
-
- /* Calculate how much data was read. */
- const size_t fixed_header_size = message_cursor.len - header_decode.len;
-
- if (result) {
- if (aws_last_error() == AWS_ERROR_SHORT_BUFFER) {
- /* Message data too short, store data and come back later. */
- AWS_LOGF_TRACE(
- AWS_LS_MQTT_CLIENT, "id=%p: message is incomplete, waiting on another read.", (void *)connection);
- if (aws_byte_buf_init(
- &connection->thread_data.pending_packet,
- connection->allocator,
- fixed_header_size + packet_header.remaining_length)) {
-
- return AWS_OP_ERR;
- }
-
- /* Write the partial packet. */
- if (!aws_byte_buf_write_from_whole_cursor(&connection->thread_data.pending_packet, message_cursor)) {
- aws_byte_buf_clean_up(&connection->thread_data.pending_packet);
- return AWS_OP_ERR;
- }
-
- aws_reset_error();
- goto cleanup;
- } else {
- return AWS_OP_ERR;
- }
- }
-
- struct aws_byte_cursor packet_data =
- aws_byte_cursor_advance(&message_cursor, fixed_header_size + packet_header.remaining_length);
- AWS_LOGF_TRACE(AWS_LS_MQTT_CLIENT, "id=%p: full mqtt packet read, dispatching.", (void *)connection);
- s_process_mqtt_packet(connection, packet_header.packet_type, packet_data);
+ if (result == AWS_OP_SUCCESS) {
+ /* Do cleanup */
+ size_t message_data_length = message->message_data.len;
+ aws_mem_release(message->allocator, message);
+ aws_channel_slot_increment_read_window(slot, message_data_length);
+ } else {
+ aws_channel_shutdown(connection->slot->channel, aws_last_error());
}
-cleanup:
- /* Do cleanup */
- aws_channel_slot_increment_read_window(slot, message->message_data.len);
- aws_mem_release(message->allocator, message);
-
- return AWS_OP_SUCCESS;
+ return result;
}
static int s_shutdown(
@@ -595,7 +635,7 @@ static int s_shutdown(
int error_code,
bool free_scarce_resources_immediately) {
- struct aws_mqtt_client_connection *connection = handler->impl;
+ struct aws_mqtt_client_connection_311_impl *connection = handler->impl;
if (dir == AWS_CHANNEL_DIR_WRITE) {
/* On closing write direction, send out disconnect packet before closing connection. */
@@ -650,7 +690,7 @@ static size_t s_initial_window_size(struct aws_channel_handler *handler) {
static void s_destroy(struct aws_channel_handler *handler) {
- struct aws_mqtt_client_connection *connection = handler->impl;
+ struct aws_mqtt_client_connection_311_impl *connection = handler->impl;
(void)connection;
}
@@ -679,7 +719,7 @@ struct aws_channel_handler_vtable *aws_mqtt_get_client_channel_vtable(void) {
******************************************************************************/
struct aws_io_message *mqtt_get_message_for_packet(
- struct aws_mqtt_client_connection *connection,
+ struct aws_mqtt_client_connection_311_impl *connection,
struct aws_mqtt_fixed_header *header) {
const size_t required_length = 3 + header->remaining_length;
@@ -704,7 +744,7 @@ struct aws_io_message *mqtt_get_message_for_packet(
static void s_request_outgoing_task(struct aws_channel_task *task, void *arg, enum aws_task_status status) {
struct aws_mqtt_request *request = arg;
- struct aws_mqtt_client_connection *connection = request->connection;
+ struct aws_mqtt_client_connection_311_impl *connection = request->connection;
if (status == AWS_TASK_STATUS_CANCELED) {
/* Connection lost before the request ever get send, check the request needs to be retried or not */
@@ -739,7 +779,7 @@ static void s_request_outgoing_task(struct aws_channel_task *task, void *arg, en
/* Fire the callback and clean up the memory, as the connection get destroyed. */
if (request->on_complete) {
request->on_complete(
- connection, request->packet_id, AWS_ERROR_MQTT_NOT_CONNECTED, request->on_complete_ud);
+ &connection->base, request->packet_id, AWS_ERROR_MQTT_NOT_CONNECTED, request->on_complete_ud);
}
{ /* BEGIN CRITICAL SECTION */
@@ -760,6 +800,8 @@ static void s_request_outgoing_task(struct aws_channel_task *task, void *arg, en
/* Send the request */
enum aws_mqtt_client_request_state state =
request->send_request(request->packet_id, !request->initiated, request->send_request_ud);
+ /* Update the request send time.*/
+ s_update_request_send_time(request);
request->initiated = true;
int error_code = AWS_ERROR_SUCCESS;
switch (state) {
@@ -783,7 +825,7 @@ static void s_request_outgoing_task(struct aws_channel_task *task, void *arg, en
/* If the send_request function reports the request is complete,
* remove from the hash table and call the callback. */
if (request->on_complete) {
- request->on_complete(connection, request->packet_id, error_code, request->on_complete_ud);
+ request->on_complete(&connection->base, request->packet_id, error_code, request->on_complete_ud);
}
{ /* BEGIN CRITICAL SECTION */
@@ -824,7 +866,7 @@ static void s_request_outgoing_task(struct aws_channel_task *task, void *arg, en
}
uint16_t mqtt_create_request(
- struct aws_mqtt_client_connection *connection,
+ struct aws_mqtt_client_connection_311_impl *connection,
aws_mqtt_send_request_fn *send_request,
void *send_request_ud,
aws_mqtt_op_complete_fn *on_complete,
@@ -962,7 +1004,7 @@ uint16_t mqtt_create_request(
return next_request->packet_id;
}
-void mqtt_request_complete(struct aws_mqtt_client_connection *connection, int error_code, uint16_t packet_id) {
+void mqtt_request_complete(struct aws_mqtt_client_connection_311_impl *connection, int error_code, uint16_t packet_id) {
AWS_LOGF_TRACE(
AWS_LS_MQTT_CLIENT,
@@ -990,6 +1032,9 @@ void mqtt_request_complete(struct aws_mqtt_client_connection *connection, int er
aws_mqtt_connection_statistics_change_operation_statistic_state(
request->connection, request, AWS_MQTT_OSS_NONE);
+ if (error_code == AWS_OP_SUCCESS) {
+ s_pushoff_next_ping_time(connection, request->request_send_timestamp);
+ }
/* clean up request resources */
aws_hash_table_remove_element(&connection->synced_data.outstanding_requests_table, elem);
/* remove the request from the list, which is thread_data.ongoing_requests_list */
@@ -1012,7 +1057,7 @@ void mqtt_request_complete(struct aws_mqtt_client_connection *connection, int er
/* Invoke the complete callback. */
if (on_complete) {
- on_complete(connection, packet_id, error_code, on_complete_ud);
+ on_complete(&connection->base, packet_id, error_code, on_complete_ud);
}
}
@@ -1026,7 +1071,7 @@ static void s_mqtt_disconnect_task(struct aws_channel_task *channel_task, void *
(void)status;
struct mqtt_shutdown_task *task = AWS_CONTAINER_OF(channel_task, struct mqtt_shutdown_task, task);
- struct aws_mqtt_client_connection *connection = arg;
+ struct aws_mqtt_client_connection_311_impl *connection = arg;
AWS_LOGF_TRACE(AWS_LS_MQTT_CLIENT, "id=%p: Doing disconnect", (void *)connection);
{ /* BEGIN CRITICAL SECTION */
@@ -1050,12 +1095,14 @@ static void s_mqtt_disconnect_task(struct aws_channel_task *channel_task, void *
aws_mem_release(connection->allocator, task);
}
-void mqtt_disconnect_impl(struct aws_mqtt_client_connection *connection, int error_code) {
+void mqtt_disconnect_impl(struct aws_mqtt_client_connection_311_impl *connection, int error_code) {
if (connection->slot) {
struct mqtt_shutdown_task *shutdown_task =
aws_mem_calloc(connection->allocator, 1, sizeof(struct mqtt_shutdown_task));
shutdown_task->error_code = error_code;
aws_channel_task_init(&shutdown_task->task, s_mqtt_disconnect_task, connection, "mqtt_disconnect");
aws_channel_schedule_task_now(connection->slot->channel, &shutdown_task->task);
+ } else {
+ AWS_LOGF_TRACE(AWS_LS_MQTT_CLIENT, "id=%p: Client currently has no slot to disconnect", (void *)connection);
}
}
diff --git a/contrib/restricted/aws/aws-c-mqtt/source/client_impl_shared.c b/contrib/restricted/aws/aws-c-mqtt/source/client_impl_shared.c
new file mode 100644
index 00000000000..6f65eb88e34
--- /dev/null
+++ b/contrib/restricted/aws/aws-c-mqtt/source/client_impl_shared.c
@@ -0,0 +1,220 @@
+/**
+ * Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved.
+ * SPDX-License-Identifier: Apache-2.0.
+ */
+
+#include <aws/mqtt/client.h>
+#include <aws/mqtt/private/client_impl_shared.h>
+
+struct aws_mqtt_client_connection *aws_mqtt_client_connection_acquire(struct aws_mqtt_client_connection *connection) {
+ if (connection != NULL) {
+ return (*connection->vtable->acquire_fn)(connection->impl);
+ }
+
+ return NULL;
+}
+
+void aws_mqtt_client_connection_release(struct aws_mqtt_client_connection *connection) {
+ if (connection != NULL) {
+ (*connection->vtable->release_fn)(connection->impl);
+ }
+}
+
+int aws_mqtt_client_connection_set_will(
+ struct aws_mqtt_client_connection *connection,
+ const struct aws_byte_cursor *topic,
+ enum aws_mqtt_qos qos,
+ bool retain,
+ const struct aws_byte_cursor *payload) {
+
+ return (*connection->vtable->set_will_fn)(connection->impl, topic, qos, retain, payload);
+}
+
+int aws_mqtt_client_connection_set_login(
+ struct aws_mqtt_client_connection *connection,
+ const struct aws_byte_cursor *username,
+ const struct aws_byte_cursor *password) {
+
+ return (*connection->vtable->set_login_fn)(connection->impl, username, password);
+}
+
+int aws_mqtt_client_connection_use_websockets(
+ struct aws_mqtt_client_connection *connection,
+ aws_mqtt_transform_websocket_handshake_fn *transformer,
+ void *transformer_ud,
+ aws_mqtt_validate_websocket_handshake_fn *validator,
+ void *validator_ud) {
+
+ return (*connection->vtable->use_websockets_fn)(
+ connection->impl, transformer, transformer_ud, validator, validator_ud);
+}
+
+int aws_mqtt_client_connection_set_http_proxy_options(
+ struct aws_mqtt_client_connection *connection,
+ struct aws_http_proxy_options *proxy_options) {
+
+ return (*connection->vtable->set_http_proxy_options_fn)(connection->impl, proxy_options);
+}
+
+int aws_mqtt_client_connection_set_host_resolution_options(
+ struct aws_mqtt_client_connection *connection,
+ const struct aws_host_resolution_config *host_resolution_config) {
+
+ return (*connection->vtable->set_host_resolution_options_fn)(connection->impl, host_resolution_config);
+}
+
+int aws_mqtt_client_connection_set_reconnect_timeout(
+ struct aws_mqtt_client_connection *connection,
+ uint64_t min_timeout,
+ uint64_t max_timeout) {
+
+ return (*connection->vtable->set_reconnect_timeout_fn)(connection->impl, min_timeout, max_timeout);
+}
+
+int aws_mqtt_client_connection_set_connection_result_handlers(
+ struct aws_mqtt_client_connection *connection,
+ aws_mqtt_client_on_connection_success_fn *on_connection_success,
+ void *on_connection_success_ud,
+ aws_mqtt_client_on_connection_failure_fn *on_connection_failure,
+ void *on_connection_failure_ud) {
+
+ return (*connection->vtable->set_connection_result_handlers)(
+ connection->impl,
+ on_connection_success,
+ on_connection_success_ud,
+ on_connection_failure,
+ on_connection_failure_ud);
+}
+
+int aws_mqtt_client_connection_set_connection_interruption_handlers(
+ struct aws_mqtt_client_connection *connection,
+ aws_mqtt_client_on_connection_interrupted_fn *on_interrupted,
+ void *on_interrupted_ud,
+ aws_mqtt_client_on_connection_resumed_fn *on_resumed,
+ void *on_resumed_ud) {
+
+ return (*connection->vtable->set_connection_interruption_handlers_fn)(
+ connection->impl, on_interrupted, on_interrupted_ud, on_resumed, on_resumed_ud);
+}
+
+int aws_mqtt_client_connection_set_connection_closed_handler(
+ struct aws_mqtt_client_connection *connection,
+ aws_mqtt_client_on_connection_closed_fn *on_closed,
+ void *on_closed_ud) {
+
+ return (*connection->vtable->set_connection_closed_handler_fn)(connection->impl, on_closed, on_closed_ud);
+}
+
+int aws_mqtt_client_connection_set_on_any_publish_handler(
+ struct aws_mqtt_client_connection *connection,
+ aws_mqtt_client_publish_received_fn *on_any_publish,
+ void *on_any_publish_ud) {
+
+ return (*connection->vtable->set_on_any_publish_handler_fn)(connection->impl, on_any_publish, on_any_publish_ud);
+}
+
+int aws_mqtt_client_connection_set_connection_termination_handler(
+ struct aws_mqtt_client_connection *connection,
+ aws_mqtt_client_on_connection_termination_fn *on_termination,
+ void *on_termination_ud) {
+
+ return (*connection->vtable->set_connection_termination_handler_fn)(
+ connection->impl, on_termination, on_termination_ud);
+}
+
+int aws_mqtt_client_connection_connect(
+ struct aws_mqtt_client_connection *connection,
+ const struct aws_mqtt_connection_options *connection_options) {
+
+ return (*connection->vtable->connect_fn)(connection->impl, connection_options);
+}
+
+int aws_mqtt_client_connection_reconnect(
+ struct aws_mqtt_client_connection *connection,
+ aws_mqtt_client_on_connection_complete_fn *on_connection_complete,
+ void *userdata) {
+
+ return (*connection->vtable->reconnect_fn)(connection->impl, on_connection_complete, userdata);
+}
+
+int aws_mqtt_client_connection_disconnect(
+ struct aws_mqtt_client_connection *connection,
+ aws_mqtt_client_on_disconnect_fn *on_disconnect,
+ void *userdata) {
+
+ return (*connection->vtable->disconnect_fn)(connection->impl, on_disconnect, userdata);
+}
+
+uint16_t aws_mqtt_client_connection_subscribe_multiple(
+ struct aws_mqtt_client_connection *connection,
+ const struct aws_array_list *topic_filters,
+ aws_mqtt_suback_multi_fn *on_suback,
+ void *on_suback_ud) {
+
+ return (*connection->vtable->subscribe_multiple_fn)(connection->impl, topic_filters, on_suback, on_suback_ud);
+}
+
+uint16_t aws_mqtt_client_connection_subscribe(
+ struct aws_mqtt_client_connection *connection,
+ const struct aws_byte_cursor *topic_filter,
+ enum aws_mqtt_qos qos,
+ aws_mqtt_client_publish_received_fn *on_publish,
+ void *on_publish_ud,
+ aws_mqtt_userdata_cleanup_fn *on_ud_cleanup,
+ aws_mqtt_suback_fn *on_suback,
+ void *on_suback_ud) {
+
+ return (*connection->vtable->subscribe_fn)(
+ connection->impl, topic_filter, qos, on_publish, on_publish_ud, on_ud_cleanup, on_suback, on_suback_ud);
+}
+
+uint16_t aws_mqtt_resubscribe_existing_topics(
+ struct aws_mqtt_client_connection *connection,
+ aws_mqtt_suback_multi_fn *on_suback,
+ void *on_suback_ud) {
+
+ return (*connection->vtable->resubscribe_existing_topics_fn)(connection->impl, on_suback, on_suback_ud);
+}
+
+uint16_t aws_mqtt_client_connection_unsubscribe(
+ struct aws_mqtt_client_connection *connection,
+ const struct aws_byte_cursor *topic_filter,
+ aws_mqtt_op_complete_fn *on_unsuback,
+ void *on_unsuback_ud) {
+
+ return (*connection->vtable->unsubscribe_fn)(connection->impl, topic_filter, on_unsuback, on_unsuback_ud);
+}
+
+uint16_t aws_mqtt_client_connection_publish(
+ struct aws_mqtt_client_connection *connection,
+ const struct aws_byte_cursor *topic,
+ enum aws_mqtt_qos qos,
+ bool retain,
+ const struct aws_byte_cursor *payload,
+ aws_mqtt_op_complete_fn *on_complete,
+ void *userdata) {
+
+ return (*connection->vtable->publish_fn)(connection->impl, topic, qos, retain, payload, on_complete, userdata);
+}
+
+int aws_mqtt_client_connection_get_stats(
+ struct aws_mqtt_client_connection *connection,
+ struct aws_mqtt_connection_operation_statistics *stats) {
+
+ return (*connection->vtable->get_stats_fn)(connection->impl, stats);
+}
+
+uint64_t aws_mqtt_hash_uint16_t(const void *item) {
+ return *(uint16_t *)item;
+}
+
+bool aws_mqtt_compare_uint16_t_eq(const void *a, const void *b) {
+ return *(uint16_t *)a == *(uint16_t *)b;
+}
+
+bool aws_mqtt_byte_cursor_hash_equality(const void *a, const void *b) {
+ const struct aws_byte_cursor *a_cursor = a;
+ const struct aws_byte_cursor *b_cursor = b;
+
+ return aws_byte_cursor_eq(a_cursor, b_cursor);
+}
diff --git a/contrib/restricted/aws/aws-c-mqtt/source/fixed_header.c b/contrib/restricted/aws/aws-c-mqtt/source/fixed_header.c
index 22372f4194a..670ae9780ea 100644
--- a/contrib/restricted/aws/aws-c-mqtt/source/fixed_header.c
+++ b/contrib/restricted/aws/aws-c-mqtt/source/fixed_header.c
@@ -29,7 +29,8 @@ static int s_encode_remaining_length(struct aws_byte_buf *buf, size_t remaining_
return AWS_OP_SUCCESS;
}
-static int s_decode_remaining_length(struct aws_byte_cursor *cur, size_t *remaining_length_out) {
+
+int aws_mqtt311_decode_remaining_length(struct aws_byte_cursor *cur, size_t *remaining_length_out) {
AWS_PRECONDITION(cur);
@@ -128,7 +129,7 @@ int aws_mqtt_fixed_header_decode(struct aws_byte_cursor *cur, struct aws_mqtt_fi
header->flags = byte_1 & 0xF;
/* Read remaining length */
- if (s_decode_remaining_length(cur, &header->remaining_length)) {
+ if (aws_mqtt311_decode_remaining_length(cur, &header->remaining_length)) {
return AWS_OP_ERR;
}
if (cur->len < header->remaining_length) {
diff --git a/contrib/restricted/aws/aws-c-mqtt/source/mqtt.c b/contrib/restricted/aws/aws-c-mqtt/source/mqtt.c
index fd39a82747f..9caeaa9bbaa 100644
--- a/contrib/restricted/aws/aws-c-mqtt/source/mqtt.c
+++ b/contrib/restricted/aws/aws-c-mqtt/source/mqtt.c
@@ -5,23 +5,28 @@
#include <aws/mqtt/mqtt.h>
+#include <aws/common/encoding.h>
+#include <aws/http/http.h>
#include <aws/io/logging.h>
-#ifdef AWS_MQTT_WITH_WEBSOCKETS
-# include <aws/http/http.h>
-#endif
-
/*******************************************************************************
* Topic Validation
******************************************************************************/
static bool s_is_valid_topic(const struct aws_byte_cursor *topic, bool is_filter) {
+ if (topic == NULL) {
+ return false;
+ }
/* [MQTT-4.7.3-1] Check existance and length */
if (!topic->ptr || !topic->len) {
return false;
}
+ if (aws_mqtt_validate_utf8_text(*topic) == AWS_OP_ERR) {
+ return false;
+ }
+
/* [MQTT-4.7.3-2] Check for the null character */
if (memchr(topic->ptr, 0, topic->len)) {
return false;
@@ -216,6 +221,21 @@ bool aws_mqtt_is_valid_topic_filter(const struct aws_byte_cursor *topic_filter)
AWS_DEFINE_ERROR_INFO_MQTT(
AWS_ERROR_MQTT5_INVALID_OUTBOUND_TOPIC_ALIAS,
"Outgoing publish contained an invalid (too large or unknown) topic alias"),
+ AWS_DEFINE_ERROR_INFO_MQTT(
+ AWS_ERROR_MQTT5_INVALID_UTF8_STRING,
+ "Outbound packet contains invalid utf-8 data in a field that must be utf-8"),
+ AWS_DEFINE_ERROR_INFO_MQTT(
+ AWS_ERROR_MQTT_CONNECTION_RESET_FOR_ADAPTER_CONNECT,
+ "Mqtt5 connection was reset by the Mqtt3 adapter in order to guarantee correct connection configuration"),
+ AWS_DEFINE_ERROR_INFO_MQTT(
+ AWS_ERROR_MQTT_CONNECTION_RESUBSCRIBE_NO_TOPICS,
+ "Resubscribe was called when there were no subscriptions"),
+ AWS_DEFINE_ERROR_INFO_MQTT(
+ AWS_ERROR_MQTT_CONNECTION_SUBSCRIBE_FAILURE,
+ "MQTT subscribe operation failed"),
+ AWS_DEFINE_ERROR_INFO_MQTT(
+ AWS_ERROR_MQTT_ACK_REASON_CODE_FAILURE,
+ "MQTT ack packet received with a failing reason code"),
};
/* clang-format on */
#undef AWS_DEFINE_ERROR_INFO_MQTT
@@ -233,6 +253,7 @@ static struct aws_error_info_list s_error_list = {
DEFINE_LOG_SUBJECT_INFO(AWS_LS_MQTT5_GENERAL, "mqtt5-general", "Misc MQTT5 logging"),
DEFINE_LOG_SUBJECT_INFO(AWS_LS_MQTT5_CLIENT, "mqtt5-client", "MQTT5 client and connections"),
DEFINE_LOG_SUBJECT_INFO(AWS_LS_MQTT5_CANARY, "mqtt5-canary", "MQTT5 canary logging"),
+ DEFINE_LOG_SUBJECT_INFO(AWS_LS_MQTT5_TO_MQTT3_ADAPTER, "mqtt5-to-mqtt3-adapter", "MQTT5-To-MQTT3 adapter logging"),
};
/* clang-format on */
@@ -250,9 +271,8 @@ void aws_mqtt_library_init(struct aws_allocator *allocator) {
if (!s_mqtt_library_initialized) {
s_mqtt_library_initialized = true;
aws_io_library_init(allocator);
-#ifdef AWS_MQTT_WITH_WEBSOCKETS
aws_http_library_init(allocator);
-#endif
+
aws_register_error_info(&s_error_list);
aws_register_log_subject_info_list(&s_logging_subjects_list);
}
@@ -264,9 +284,8 @@ void aws_mqtt_library_clean_up(void) {
aws_thread_join_all_managed();
aws_unregister_error_info(&s_error_list);
aws_unregister_log_subject_info_list(&s_logging_subjects_list);
-#ifdef AWS_MQTT_WITH_WEBSOCKETS
+
aws_http_library_clean_up();
-#endif
aws_io_library_clean_up();
}
}
@@ -280,3 +299,37 @@ void aws_mqtt_fatal_assert_library_initialized(void) {
AWS_FATAL_ASSERT(s_mqtt_library_initialized);
}
}
+
+/* UTF-8 encoded string validation respect to [MQTT-1.5.3-2]. */
+static int aws_mqtt_utf8_decoder(uint32_t codepoint, void *user_data) {
+ (void)user_data;
+ /* U+0000 - A UTF-8 Encoded String MUST NOT include an encoding of the null character U+0000. [MQTT-1.5.4-2]
+ * U+0001..U+001F control characters are not valid
+ */
+ if (AWS_UNLIKELY(codepoint <= 0x001F)) {
+ return aws_raise_error(AWS_ERROR_MQTT5_INVALID_UTF8_STRING);
+ }
+
+ /* U+007F..U+009F control characters are not valid */
+ if (AWS_UNLIKELY((codepoint >= 0x007F) && (codepoint <= 0x009F))) {
+ return aws_raise_error(AWS_ERROR_MQTT5_INVALID_UTF8_STRING);
+ }
+
+ /* Unicode non-characters are not valid: https://www.unicode.org/faq/private_use.html#nonchar1 */
+ if (AWS_UNLIKELY((codepoint & 0x00FFFF) >= 0x00FFFE)) {
+ return aws_raise_error(AWS_ERROR_MQTT5_INVALID_UTF8_STRING);
+ }
+ if (AWS_UNLIKELY(codepoint >= 0xFDD0 && codepoint <= 0xFDEF)) {
+ return aws_raise_error(AWS_ERROR_MQTT5_INVALID_UTF8_STRING);
+ }
+
+ return AWS_OP_SUCCESS;
+}
+
+static struct aws_utf8_decoder_options s_aws_mqtt_utf8_decoder_options = {
+ .on_codepoint = aws_mqtt_utf8_decoder,
+};
+
+int aws_mqtt_validate_utf8_text(struct aws_byte_cursor text) {
+ return aws_decode_utf8(text, &s_aws_mqtt_utf8_decoder_options);
+}
diff --git a/contrib/restricted/aws/aws-c-mqtt/source/mqtt311_decoder.c b/contrib/restricted/aws/aws-c-mqtt/source/mqtt311_decoder.c
new file mode 100644
index 00000000000..cb1f26c47e2
--- /dev/null
+++ b/contrib/restricted/aws/aws-c-mqtt/source/mqtt311_decoder.c
@@ -0,0 +1,211 @@
+/**
+ * Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved.
+ * SPDX-License-Identifier: Apache-2.0.
+ */
+
+#include <aws/mqtt/private/mqtt311_decoder.h>
+
+#include <aws/mqtt/private/fixed_header.h>
+
+static void s_aws_mqtt311_decoder_reset(struct aws_mqtt311_decoder *decoder) {
+ decoder->state = AWS_MDST_READ_FIRST_BYTE;
+ decoder->total_packet_length = 0;
+ aws_byte_buf_reset(&decoder->packet_buffer, false);
+}
+
+void aws_mqtt311_decoder_init(
+ struct aws_mqtt311_decoder *decoder,
+ struct aws_allocator *allocator,
+ const struct aws_mqtt311_decoder_options *options) {
+
+ aws_byte_buf_init(&decoder->packet_buffer, allocator, 5);
+ decoder->config = *options;
+
+ s_aws_mqtt311_decoder_reset(decoder);
+}
+
+void aws_mqtt311_decoder_clean_up(struct aws_mqtt311_decoder *decoder) {
+ aws_byte_buf_clean_up(&decoder->packet_buffer);
+}
+
+static void s_aws_mqtt311_decoder_reset_for_new_packet(struct aws_mqtt311_decoder *decoder) {
+ if (decoder->state != AWS_MDST_PROTOCOL_ERROR) {
+ s_aws_mqtt311_decoder_reset(decoder);
+ }
+}
+
+enum aws_mqtt311_decoding_directive { AWS_MDD_CONTINUE, AWS_MDD_OUT_OF_DATA, AWS_MDD_PROTOCOL_ERROR };
+
+static enum aws_mqtt311_decoding_directive aws_result_to_mqtt311_decoding_directive(int result) {
+ return (result == AWS_OP_SUCCESS) ? AWS_MDD_CONTINUE : AWS_MDD_PROTOCOL_ERROR;
+}
+
+static int s_aws_mqtt311_decoder_safe_packet_handle(
+ struct aws_mqtt311_decoder *decoder,
+ enum aws_mqtt_packet_type packet_type,
+ struct aws_byte_cursor packet_cursor) {
+ packet_handler_fn *handler = decoder->config.packet_handlers->handlers_by_packet_type[packet_type];
+ if (handler != NULL) {
+ return handler(packet_cursor, decoder->config.handler_user_data);
+ } else {
+ return aws_raise_error(AWS_ERROR_MQTT_PROTOCOL_ERROR);
+ }
+}
+
+static enum aws_mqtt311_decoding_directive s_handle_decoder_read_first_byte(
+ struct aws_mqtt311_decoder *decoder,
+ struct aws_byte_cursor *data) {
+ AWS_FATAL_ASSERT(decoder->packet_buffer.len == 0);
+ if (data->len == 0) {
+ return AWS_MDD_OUT_OF_DATA;
+ }
+
+ /*
+ * Do a greedy check to see if the whole MQTT packet is contained within the received data. If it is, decode it
+ * directly from the incoming data cursor without buffering it first. Otherwise, the packet is fragmented
+ * across multiple received data calls, and so we must use the packet buffer and copy everything first via the
+ * full decoder state machine.
+ *
+ * A corollary of this is that the decoder is only ever in the AWS_MDST_READ_REMAINING_LENGTH or AWS_MDST_READ_BODY
+ * states if the current MQTT packet was received in a fragmented manner.
+ */
+ struct aws_byte_cursor temp_cursor = *data;
+ struct aws_mqtt_fixed_header packet_header;
+ AWS_ZERO_STRUCT(packet_header);
+ if (!aws_mqtt_fixed_header_decode(&temp_cursor, &packet_header) &&
+ temp_cursor.len >= packet_header.remaining_length) {
+
+ /* figure out the cursor that spans the full packet */
+ size_t fixed_header_length = temp_cursor.ptr - data->ptr;
+ struct aws_byte_cursor whole_packet_cursor = *data;
+ whole_packet_cursor.len = fixed_header_length + packet_header.remaining_length;
+
+ /* advance the external, mutable data cursor to the start of the next packet */
+ aws_byte_cursor_advance(data, whole_packet_cursor.len);
+
+ /*
+ * if this fails, the decoder goes into an error state. If it succeeds we'll loop again into the same state
+ * because we'll be back at the beginning of the next packet (if it exists).
+ */
+ enum aws_mqtt_packet_type packet_type = aws_mqtt_get_packet_type(whole_packet_cursor.ptr);
+ return aws_result_to_mqtt311_decoding_directive(
+ s_aws_mqtt311_decoder_safe_packet_handle(decoder, packet_type, whole_packet_cursor));
+ }
+
+ /*
+ * The packet is fragmented, spanning more than this io message. So we buffer it and use the
+ * simple state machine to decode.
+ */
+ uint8_t byte = *data->ptr;
+ aws_byte_cursor_advance(data, 1);
+ aws_byte_buf_append_byte_dynamic(&decoder->packet_buffer, byte);
+
+ decoder->state = AWS_MDST_READ_REMAINING_LENGTH;
+
+ return AWS_MDD_CONTINUE;
+}
+
+static enum aws_mqtt311_decoding_directive s_handle_decoder_read_remaining_length(
+ struct aws_mqtt311_decoder *decoder,
+ struct aws_byte_cursor *data) {
+ AWS_FATAL_ASSERT(decoder->total_packet_length == 0);
+ if (data->len == 0) {
+ return AWS_MDD_OUT_OF_DATA;
+ }
+
+ uint8_t byte = *data->ptr;
+ aws_byte_cursor_advance(data, 1);
+ aws_byte_buf_append_byte_dynamic(&decoder->packet_buffer, byte);
+
+ struct aws_byte_cursor vli_cursor = aws_byte_cursor_from_buf(&decoder->packet_buffer);
+ aws_byte_cursor_advance(&vli_cursor, 1);
+
+ size_t remaining_length = 0;
+ if (aws_mqtt311_decode_remaining_length(&vli_cursor, &remaining_length) == AWS_OP_ERR) {
+ /* anything other than a short buffer error (not enough data yet) is a terminal error */
+ if (aws_last_error() == AWS_ERROR_SHORT_BUFFER) {
+ return AWS_MDD_CONTINUE;
+ } else {
+ return AWS_MDD_PROTOCOL_ERROR;
+ }
+ }
+
+ /*
+ * If we successfully decoded a variable-length integer, we now know exactly how many bytes we need to receive in
+ * order to have the full packet.
+ */
+ decoder->total_packet_length = remaining_length + decoder->packet_buffer.len;
+ AWS_FATAL_ASSERT(decoder->total_packet_length > 0);
+ decoder->state = AWS_MDST_READ_BODY;
+
+ return AWS_MDD_CONTINUE;
+}
+
+static enum aws_mqtt311_decoding_directive s_handle_decoder_read_body(
+ struct aws_mqtt311_decoder *decoder,
+ struct aws_byte_cursor *data) {
+ AWS_FATAL_ASSERT(decoder->total_packet_length > 0);
+
+ size_t buffer_length = decoder->packet_buffer.len;
+ size_t amount_to_read = aws_min_size(decoder->total_packet_length - buffer_length, data->len);
+
+ struct aws_byte_cursor copy_cursor = aws_byte_cursor_advance(data, amount_to_read);
+ aws_byte_buf_append_dynamic(&decoder->packet_buffer, &copy_cursor);
+
+ if (decoder->packet_buffer.len == decoder->total_packet_length) {
+
+ /* We have the full packet in the scratch buffer, invoke the correct handler to decode and process it */
+ struct aws_byte_cursor packet_data = aws_byte_cursor_from_buf(&decoder->packet_buffer);
+ enum aws_mqtt_packet_type packet_type = aws_mqtt_get_packet_type(packet_data.ptr);
+ if (s_aws_mqtt311_decoder_safe_packet_handle(decoder, packet_type, packet_data) == AWS_OP_ERR) {
+ return AWS_MDD_PROTOCOL_ERROR;
+ }
+
+ s_aws_mqtt311_decoder_reset_for_new_packet(decoder);
+ return AWS_MDD_CONTINUE;
+ }
+
+ return AWS_MDD_OUT_OF_DATA;
+}
+
+int aws_mqtt311_decoder_on_bytes_received(struct aws_mqtt311_decoder *decoder, struct aws_byte_cursor data) {
+ struct aws_byte_cursor data_cursor = data;
+
+ enum aws_mqtt311_decoding_directive decode_directive = AWS_MDD_CONTINUE;
+ while (decode_directive == AWS_MDD_CONTINUE) {
+ switch (decoder->state) {
+ case AWS_MDST_READ_FIRST_BYTE:
+ decode_directive = s_handle_decoder_read_first_byte(decoder, &data_cursor);
+ break;
+
+ case AWS_MDST_READ_REMAINING_LENGTH:
+ decode_directive = s_handle_decoder_read_remaining_length(decoder, &data_cursor);
+ break;
+
+ case AWS_MDST_READ_BODY:
+ decode_directive = s_handle_decoder_read_body(decoder, &data_cursor);
+ break;
+
+ default:
+ decode_directive = AWS_MDD_PROTOCOL_ERROR;
+ break;
+ }
+
+ /*
+ * Protocol error is a terminal failure state until aws_mqtt311_decoder_reset_for_new_connection() is called.
+ */
+ if (decode_directive == AWS_MDD_PROTOCOL_ERROR) {
+ decoder->state = AWS_MDST_PROTOCOL_ERROR;
+ if (aws_last_error() == AWS_ERROR_SUCCESS) {
+ aws_raise_error(AWS_ERROR_MQTT_PROTOCOL_ERROR);
+ }
+ return AWS_OP_ERR;
+ }
+ }
+
+ return AWS_OP_SUCCESS;
+}
+
+void aws_mqtt311_decoder_reset_for_new_connection(struct aws_mqtt311_decoder *decoder) {
+ s_aws_mqtt311_decoder_reset(decoder);
+}
diff --git a/contrib/restricted/aws/aws-c-mqtt/source/mqtt_subscription_set.c b/contrib/restricted/aws/aws-c-mqtt/source/mqtt_subscription_set.c
new file mode 100644
index 00000000000..1e946fc5fed
--- /dev/null
+++ b/contrib/restricted/aws/aws-c-mqtt/source/mqtt_subscription_set.c
@@ -0,0 +1,431 @@
+/**
+ * Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved.
+ * SPDX-License-Identifier: Apache-2.0.
+ */
+
+#include "aws/mqtt/private/mqtt_subscription_set.h"
+
+#include "aws/mqtt/private/client_impl_shared.h"
+
+#define SUBSCRIPTION_SET_DEFAULT_BRANCH_FACTOR 10
+#define SUBSCRIPTION_SET_DEFAULT_ENTRY_COUNT 50
+
+struct aws_mqtt_subscription_set_subscription_record *aws_mqtt_subscription_set_subscription_record_new(
+ struct aws_allocator *allocator,
+ const struct aws_mqtt_subscription_set_subscription_options *subscription) {
+ struct aws_mqtt_subscription_set_subscription_record *record =
+ aws_mem_calloc(allocator, 1, sizeof(struct aws_mqtt_subscription_set_subscription_record));
+
+ record->allocator = allocator;
+ aws_byte_buf_init_copy_from_cursor(&record->topic_filter, allocator, subscription->topic_filter);
+ record->subscription_view = *subscription;
+ record->subscription_view.topic_filter = aws_byte_cursor_from_buf(&record->topic_filter);
+
+ return record;
+}
+
+void aws_mqtt_subscription_set_subscription_record_destroy(
+ struct aws_mqtt_subscription_set_subscription_record *record) {
+ if (record == NULL) {
+ return;
+ }
+
+ aws_byte_buf_clean_up(&record->topic_filter);
+ aws_mem_release(record->allocator, record);
+}
+
+static void s_aws_mqtt_subscription_set_subscription_record_hash_destroy(void *element) {
+ struct aws_mqtt_subscription_set_subscription_record *record = element;
+
+ aws_mqtt_subscription_set_subscription_record_destroy(record);
+}
+
+static struct aws_mqtt_subscription_set_topic_tree_node *s_aws_mqtt_subscription_set_node_new(
+ struct aws_allocator *allocator,
+ struct aws_mqtt_subscription_set_topic_tree_node *parent) {
+
+ struct aws_mqtt_subscription_set_topic_tree_node *node =
+ aws_mem_calloc(allocator, 1, sizeof(struct aws_mqtt_subscription_set_topic_tree_node));
+ node->allocator = allocator;
+ aws_hash_table_init(
+ &node->children,
+ allocator,
+ SUBSCRIPTION_SET_DEFAULT_BRANCH_FACTOR,
+ aws_hash_byte_cursor_ptr,
+ aws_mqtt_byte_cursor_hash_equality,
+ NULL,
+ NULL);
+ node->ref_count = 1;
+ node->parent = parent;
+
+ return node;
+}
+
+struct aws_mqtt_subscription_set *aws_mqtt_subscription_set_new(struct aws_allocator *allocator) {
+
+ struct aws_mqtt_subscription_set *subscription_set =
+ aws_mem_calloc(allocator, 1, sizeof(struct aws_mqtt_subscription_set));
+
+ subscription_set->allocator = allocator;
+ subscription_set->root = s_aws_mqtt_subscription_set_node_new(allocator, NULL);
+
+ aws_hash_table_init(
+ &subscription_set->subscriptions,
+ allocator,
+ SUBSCRIPTION_SET_DEFAULT_ENTRY_COUNT,
+ aws_hash_byte_cursor_ptr,
+ aws_mqtt_byte_cursor_hash_equality,
+ NULL,
+ s_aws_mqtt_subscription_set_subscription_record_hash_destroy);
+
+ return subscription_set;
+}
+
+static int s_subscription_set_node_destroy_hash_foreach_wrap(void *context, struct aws_hash_element *elem);
+
+static void s_aws_mqtt_subscription_set_node_destroy_node(struct aws_mqtt_subscription_set_topic_tree_node *node) {
+ aws_hash_table_foreach(&node->children, s_subscription_set_node_destroy_hash_foreach_wrap, NULL);
+ aws_hash_table_clean_up(&node->children);
+
+ if (node->on_cleanup && node->callback_user_data) {
+ node->on_cleanup(node->callback_user_data);
+ }
+
+ aws_byte_buf_clean_up(&node->topic_segment);
+
+ aws_mem_release(node->allocator, node);
+}
+
+static void s_aws_mqtt_subscription_set_node_destroy_tree(struct aws_mqtt_subscription_set_topic_tree_node *tree) {
+ if (tree == NULL) {
+ return;
+ }
+
+ if (tree->parent != NULL) {
+ aws_hash_table_remove(&tree->parent->children, &tree->topic_segment, NULL, NULL);
+ }
+
+ s_aws_mqtt_subscription_set_node_destroy_node(tree);
+}
+
+static int s_subscription_set_node_destroy_hash_foreach_wrap(void *context, struct aws_hash_element *elem) {
+ (void)context;
+
+ s_aws_mqtt_subscription_set_node_destroy_node(elem->value);
+
+ return AWS_COMMON_HASH_TABLE_ITER_CONTINUE | AWS_COMMON_HASH_TABLE_ITER_DELETE;
+}
+
+void aws_mqtt_subscription_set_destroy(struct aws_mqtt_subscription_set *subscription_set) {
+ if (subscription_set == NULL) {
+ return;
+ }
+
+ s_aws_mqtt_subscription_set_node_destroy_tree(subscription_set->root);
+
+ aws_hash_table_clean_up(&subscription_set->subscriptions);
+
+ aws_mem_release(subscription_set->allocator, subscription_set);
+}
+
+static struct aws_mqtt_subscription_set_topic_tree_node *s_aws_mqtt_subscription_set_get_existing_subscription_node(
+ const struct aws_mqtt_subscription_set *subscription_set,
+ struct aws_byte_cursor topic_filter) {
+
+ struct aws_mqtt_subscription_set_topic_tree_node *current_node = subscription_set->root;
+
+ struct aws_byte_cursor topic_segment;
+ AWS_ZERO_STRUCT(topic_segment);
+ while (aws_byte_cursor_next_split(&topic_filter, '/', &topic_segment)) {
+ struct aws_hash_element *hash_element = NULL;
+ aws_hash_table_find(&current_node->children, &topic_segment, &hash_element);
+
+ if (hash_element == NULL) {
+ return NULL;
+ } else {
+ current_node = hash_element->value;
+ }
+ }
+
+ if (!current_node->is_subscription) {
+ return NULL;
+ }
+
+ return current_node;
+}
+
+bool aws_mqtt_subscription_set_is_subscribed(
+ const struct aws_mqtt_subscription_set *subscription_set,
+ struct aws_byte_cursor topic_filter) {
+
+ struct aws_hash_element *element = NULL;
+ aws_hash_table_find(&subscription_set->subscriptions, &topic_filter, &element);
+
+ return element && (element->value != NULL);
+}
+
+bool aws_mqtt_subscription_set_is_in_topic_tree(
+ const struct aws_mqtt_subscription_set *subscription_set,
+ struct aws_byte_cursor topic_filter) {
+ struct aws_mqtt_subscription_set_topic_tree_node *existing_node =
+ s_aws_mqtt_subscription_set_get_existing_subscription_node(subscription_set, topic_filter);
+
+ return existing_node != NULL;
+}
+
+/*
+ * Walks the existing tree creating nodes as necessary to reach the subscription leaf implied by the topic filter.
+ * Returns the node representing the final level of the topic filter. Each existing node has its ref count increased by
+ * one. Newly-created nodes start with a ref count of one. Given that the topic filter has been validated, the only
+ * possible error is a memory allocation error which is a crash anyways.
+ *
+ * If the leaf node already exists and has a cleanup callback, it will be invoked and both the callback and its user
+ * data will be cleared . The returned node will always have is_subscription set to true.
+ */
+static struct aws_mqtt_subscription_set_topic_tree_node *
+ s_aws_mqtt_subscription_set_create_or_reference_topic_filter_path(
+ struct aws_mqtt_subscription_set_topic_tree_node *root,
+ struct aws_byte_cursor topic_filter) {
+
+ struct aws_mqtt_subscription_set_topic_tree_node *current_node = root;
+ ++root->ref_count;
+
+ /*
+ * Invariants:
+ * (1) No failure allowed (allocation failure = crash)
+ * (2) The ref count of current_node is always correct *before* the loop condition is evaluated
+ */
+ struct aws_byte_cursor topic_segment;
+ AWS_ZERO_STRUCT(topic_segment);
+ while (aws_byte_cursor_next_split(&topic_filter, '/', &topic_segment)) {
+
+ struct aws_hash_element *hash_element = NULL;
+ aws_hash_table_find(&current_node->children, &topic_segment, &hash_element);
+
+ if (hash_element == NULL) {
+ struct aws_mqtt_subscription_set_topic_tree_node *new_node =
+ s_aws_mqtt_subscription_set_node_new(current_node->allocator, current_node);
+
+ aws_byte_buf_init_copy_from_cursor(&new_node->topic_segment, new_node->allocator, topic_segment);
+ new_node->topic_segment_cursor = aws_byte_cursor_from_buf(&new_node->topic_segment);
+
+ aws_hash_table_put(&current_node->children, &new_node->topic_segment_cursor, new_node, NULL);
+
+ current_node = new_node;
+ } else {
+ current_node = hash_element->value;
+ ++current_node->ref_count;
+ }
+ }
+
+ return current_node;
+}
+
+void aws_mqtt_subscription_set_add_subscription(
+ struct aws_mqtt_subscription_set *subscription_set,
+ const struct aws_mqtt_subscription_set_subscription_options *subscription_options) {
+
+ AWS_FATAL_ASSERT(aws_mqtt_is_valid_topic_filter(&subscription_options->topic_filter));
+
+ aws_hash_table_remove(&subscription_set->subscriptions, &subscription_options->topic_filter, NULL, NULL);
+
+ struct aws_mqtt_subscription_set_subscription_record *record =
+ aws_mqtt_subscription_set_subscription_record_new(subscription_set->allocator, subscription_options);
+ aws_hash_table_put(&subscription_set->subscriptions, &record->topic_filter, record, NULL);
+
+ struct aws_mqtt_subscription_set_topic_tree_node *subscription_node =
+ s_aws_mqtt_subscription_set_get_existing_subscription_node(
+ subscription_set, subscription_options->topic_filter);
+ if (subscription_node == NULL) {
+ subscription_node = s_aws_mqtt_subscription_set_create_or_reference_topic_filter_path(
+ subscription_set->root, subscription_options->topic_filter);
+ }
+
+ if (subscription_node->on_cleanup) {
+ (*subscription_node->on_cleanup)(subscription_node->callback_user_data);
+ subscription_node->on_cleanup = NULL;
+ }
+
+ subscription_node->is_subscription = true;
+
+ subscription_node->on_publish_received = subscription_options->on_publish_received;
+ subscription_node->on_cleanup = subscription_options->on_cleanup;
+ subscription_node->callback_user_data = subscription_options->callback_user_data;
+}
+
+void aws_mqtt_subscription_set_remove_subscription(
+ struct aws_mqtt_subscription_set *subscription_set,
+ struct aws_byte_cursor topic_filter) {
+
+ aws_hash_table_remove(&subscription_set->subscriptions, &topic_filter, NULL, NULL);
+
+ if (!aws_mqtt_subscription_set_is_in_topic_tree(subscription_set, topic_filter)) {
+ return;
+ }
+
+ struct aws_mqtt_subscription_set_topic_tree_node *current_node = subscription_set->root;
+
+ struct aws_byte_cursor topic_segment;
+ AWS_ZERO_STRUCT(topic_segment);
+ while (aws_byte_cursor_next_split(&topic_filter, '/', &topic_segment)) {
+ --current_node->ref_count;
+
+ if (current_node->ref_count == 0) {
+ s_aws_mqtt_subscription_set_node_destroy_tree(current_node);
+ return;
+ }
+
+ struct aws_hash_element *hash_element = NULL;
+ aws_hash_table_find(&current_node->children, &topic_segment, &hash_element);
+
+ /* We previously validated the full path; this must exist */
+ current_node = hash_element->value;
+ }
+
+ --current_node->ref_count;
+ if (current_node->ref_count == 0) {
+ s_aws_mqtt_subscription_set_node_destroy_tree(current_node);
+ return;
+ }
+
+ if (current_node->on_cleanup) {
+ (*current_node->on_cleanup)(current_node->callback_user_data);
+ current_node->on_cleanup = NULL;
+ }
+
+ current_node->on_publish_received = NULL;
+ current_node->is_subscription = false;
+}
+
+struct aws_mqtt_subscription_set_path_continuation {
+ struct aws_byte_cursor current_fragment;
+ struct aws_mqtt_subscription_set_topic_tree_node *current_node;
+};
+
+static void s_add_subscription_set_path_continuation(
+ struct aws_array_list *paths,
+ struct aws_byte_cursor fragment,
+ struct aws_mqtt_subscription_set_topic_tree_node *node) {
+ if (node == NULL) {
+ return;
+ }
+
+ struct aws_mqtt_subscription_set_path_continuation path = {
+ .current_fragment = fragment,
+ .current_node = node,
+ };
+
+ aws_array_list_push_back(paths, &path);
+}
+
+#define SUBSCRIPTION_SET_PATH_FRAGMENT_DEFAULT 10
+
+AWS_STATIC_STRING_FROM_LITERAL(s_single_level_wildcard, "+");
+AWS_STATIC_STRING_FROM_LITERAL(s_multi_level_wildcard, "#");
+
+static struct aws_mqtt_subscription_set_topic_tree_node *s_aws_mqtt_subscription_set_node_find_child(
+ struct aws_mqtt_subscription_set_topic_tree_node *node,
+ struct aws_byte_cursor fragment) {
+ struct aws_hash_element *element = NULL;
+ aws_hash_table_find(&node->children, &fragment, &element);
+
+ if (element == NULL) {
+ return NULL;
+ }
+
+ return element->value;
+}
+
+static void s_invoke_on_publish_received(
+ struct aws_mqtt_subscription_set_topic_tree_node *node,
+ const struct aws_mqtt_subscription_set_publish_received_options *publish_options) {
+ if (node == NULL || !node->is_subscription || node->on_publish_received == NULL) {
+ return;
+ }
+
+ (*node->on_publish_received)(
+ publish_options->connection,
+ &publish_options->topic,
+ &publish_options->payload,
+ publish_options->dup,
+ publish_options->qos,
+ publish_options->retain,
+ node->callback_user_data);
+}
+
+void aws_mqtt_subscription_set_on_publish_received(
+ const struct aws_mqtt_subscription_set *subscription_set,
+ const struct aws_mqtt_subscription_set_publish_received_options *publish_options) {
+
+ struct aws_byte_cursor slw_cursor = aws_byte_cursor_from_string(s_single_level_wildcard);
+ struct aws_byte_cursor mlw_cursor = aws_byte_cursor_from_string(s_multi_level_wildcard);
+
+ struct aws_array_list tree_paths;
+ aws_array_list_init_dynamic(
+ &tree_paths,
+ subscription_set->allocator,
+ SUBSCRIPTION_SET_PATH_FRAGMENT_DEFAULT,
+ sizeof(struct aws_mqtt_subscription_set_path_continuation));
+
+ struct aws_byte_cursor empty_cursor;
+ AWS_ZERO_STRUCT(empty_cursor);
+ s_add_subscription_set_path_continuation(&tree_paths, empty_cursor, subscription_set->root);
+
+ while (aws_array_list_length(&tree_paths) > 0) {
+ struct aws_mqtt_subscription_set_path_continuation path_continuation;
+ AWS_ZERO_STRUCT(path_continuation);
+
+ size_t path_count = aws_array_list_length(&tree_paths);
+ aws_array_list_get_at(&tree_paths, &path_continuation, path_count - 1);
+ aws_array_list_pop_back(&tree_paths);
+
+ /*
+ * Invoke multi-level wildcard check before checking split result; this allows a subscription like
+ * 'a/b/#' to match an incoming 'a/b'
+ */
+ struct aws_mqtt_subscription_set_topic_tree_node *mlw_node =
+ s_aws_mqtt_subscription_set_node_find_child(path_continuation.current_node, mlw_cursor);
+ s_invoke_on_publish_received(mlw_node, publish_options);
+
+ struct aws_byte_cursor next_fragment = path_continuation.current_fragment;
+ if (!aws_byte_cursor_next_split(&publish_options->topic, '/', &next_fragment)) {
+ s_invoke_on_publish_received(path_continuation.current_node, publish_options);
+ continue;
+ }
+
+ struct aws_mqtt_subscription_set_topic_tree_node *slw_node =
+ s_aws_mqtt_subscription_set_node_find_child(path_continuation.current_node, slw_cursor);
+ s_add_subscription_set_path_continuation(&tree_paths, next_fragment, slw_node);
+
+ struct aws_mqtt_subscription_set_topic_tree_node *fragment_node =
+ s_aws_mqtt_subscription_set_node_find_child(path_continuation.current_node, next_fragment);
+ s_add_subscription_set_path_continuation(&tree_paths, next_fragment, fragment_node);
+ }
+
+ aws_array_list_clean_up(&tree_paths);
+}
+
+static int s_subscription_set_subscriptions_hash_get_wrap(void *context, struct aws_hash_element *elem) {
+ struct aws_array_list *subscriptions = context;
+ struct aws_mqtt_subscription_set_subscription_record *record = elem->value;
+
+ aws_array_list_push_back(subscriptions, &record->subscription_view);
+
+ return AWS_COMMON_HASH_TABLE_ITER_CONTINUE;
+}
+
+void aws_mqtt_subscription_set_get_subscriptions(
+ struct aws_mqtt_subscription_set *subscription_set,
+ struct aws_array_list *subscriptions) {
+ AWS_ZERO_STRUCT(*subscriptions);
+
+ size_t subscription_count = aws_hash_table_get_entry_count(&subscription_set->subscriptions);
+ aws_array_list_init_dynamic(
+ subscriptions,
+ subscription_set->allocator,
+ subscription_count,
+ sizeof(struct aws_mqtt_subscription_set_subscription_options));
+
+ aws_hash_table_foreach(
+ &subscription_set->subscriptions, s_subscription_set_subscriptions_hash_get_wrap, subscriptions);
+}
diff --git a/contrib/restricted/aws/aws-c-mqtt/source/packets.c b/contrib/restricted/aws/aws-c-mqtt/source/packets.c
index 7af2af2673e..1170bcca9c6 100644
--- a/contrib/restricted/aws/aws-c-mqtt/source/packets.c
+++ b/contrib/restricted/aws/aws-c-mqtt/source/packets.c
@@ -595,6 +595,10 @@ bool aws_mqtt_packet_publish_get_dup(const struct aws_mqtt_packet_publish *packe
return packet->fixed_header.flags & (1 << 3); /* bit 3 */
}
+void aws_mqtt_packet_publish_set_dup(struct aws_mqtt_packet_publish *packet) {
+ packet->fixed_header.flags |= 0x08;
+}
+
enum aws_mqtt_qos aws_mqtt_packet_publish_get_qos(const struct aws_mqtt_packet_publish *packet) {
return (packet->fixed_header.flags >> 1) & 0x3; /* bits 2,1 */
}
diff --git a/contrib/restricted/aws/aws-c-mqtt/source/topic_tree.c b/contrib/restricted/aws/aws-c-mqtt/source/topic_tree.c
index ca75c9028e4..d2104ed322c 100644
--- a/contrib/restricted/aws/aws-c-mqtt/source/topic_tree.c
+++ b/contrib/restricted/aws/aws-c-mqtt/source/topic_tree.c
@@ -433,6 +433,8 @@ static void s_topic_tree_action_commit(struct topic_tree_action *action, struct
/* Clean up and delete */
s_topic_node_destroy(node, tree->allocator);
} else {
+ // We do not delete the current node immediately as we would like to use
+ // it to update the topic filter of the remaining nodes
destroy_current = true;
}
} else {
@@ -450,8 +452,9 @@ static void s_topic_tree_action_commit(struct topic_tree_action *action, struct
}
}
- /* If current owns the full string, go fixup the pointer references. */
- if (nodes_left > 0) {
+ /* If at least one node is destroyed and there is node(s) remaining in the branch,
+ * go fixup the topic filter reference . */
+ if (nodes_left > 0 && destroy_current) {
/* If a new viable topic filter is found once, it can be used for all parents. */
const struct aws_string *new_topic_filter = NULL;
@@ -465,7 +468,7 @@ static void s_topic_tree_action_commit(struct topic_tree_action *action, struct
size_t topic_offset =
parent->topic.ptr - aws_string_bytes(parent->topic_filter) + parent->topic.len + 1;
- /* -1 to avoid touching current */
+ /* Loop through all remaining nodes to update the topic filters */
for (size_t i = nodes_left; i > 0; --i) {
aws_array_list_get_at(&action->to_remove, &parent, i);
AWS_ASSUME(parent); /* Must be in bounds */
@@ -493,7 +496,8 @@ static void s_topic_tree_action_commit(struct topic_tree_action *action, struct
&parent->subtopics, s_topic_node_string_finder, (void *)&new_topic_filter);
/* This would only happen if there is only one topic in subtopics (current's) and
- * it has no children (in which case it should have been removed above). */
+ * it has no children (in which case it should have been removed above as
+ `destroy_current` is set to true). */
AWS_ASSERT(new_topic_filter != old_topic_filter);
/* Now that the new string has been found, the old one can be destroyed. */
diff --git a/contrib/restricted/aws/aws-c-mqtt/source/v5/mqtt5_client.c b/contrib/restricted/aws/aws-c-mqtt/source/v5/mqtt5_client.c
index 27af76038e6..e6da6d3d397 100644
--- a/contrib/restricted/aws/aws-c-mqtt/source/v5/mqtt5_client.c
+++ b/contrib/restricted/aws/aws-c-mqtt/source/v5/mqtt5_client.c
@@ -12,6 +12,7 @@
#include <aws/http/websocket.h>
#include <aws/io/channel_bootstrap.h>
#include <aws/io/event_loop.h>
+#include <aws/mqtt/private/client_impl_shared.h>
#include <aws/mqtt/private/shared_constants.h>
#include <aws/mqtt/private/v5/mqtt5_client_impl.h>
#include <aws/mqtt/private/v5/mqtt5_options_storage.h>
@@ -26,6 +27,7 @@
#define AWS_MQTT5_IO_MESSAGE_DEFAULT_LENGTH 4096
#define AWS_MQTT5_DEFAULT_CONNACK_PACKET_TIMEOUT_MS 10000
+#define DEFAULT_MQTT5_OPERATION_TABLE_SIZE 200
const char *aws_mqtt5_client_state_to_c_string(enum aws_mqtt5_client_state state) {
switch (state) {
@@ -156,14 +158,6 @@ static int s_aws_mqtt5_client_change_desired_state(
enum aws_mqtt5_client_state desired_state,
struct aws_mqtt5_operation_disconnect *disconnect_operation);
-static uint64_t s_hash_uint16_t(const void *item) {
- return *(uint16_t *)item;
-}
-
-static bool s_uint16_t_eq(const void *a, const void *b) {
- return *(uint16_t *)a == *(uint16_t *)b;
-}
-
static uint64_t s_aws_mqtt5_client_compute_operational_state_service_time(
const struct aws_mqtt5_client_operational_state *client_operational_state,
uint64_t now);
@@ -178,6 +172,13 @@ static void s_complete_operation(
const void *view) {
if (client != NULL) {
aws_mqtt5_client_statistics_change_operation_statistic_state(client, operation, AWS_MQTT5_OSS_NONE);
+ if (aws_priority_queue_node_is_in_queue(&operation->priority_queue_node)) {
+ struct aws_mqtt5_operation *queued_operation = NULL;
+ aws_priority_queue_remove(
+ &client->operational_state.operations_by_ack_timeout,
+ &queued_operation,
+ &operation->priority_queue_node);
+ }
}
aws_mqtt5_operation_complete(operation, error_code, packet_type, view);
@@ -203,72 +204,47 @@ static void s_complete_operation_list(
}
static void s_check_timeouts(struct aws_mqtt5_client *client, uint64_t now) {
- if (client->config->ack_timeout_seconds == 0) {
- return;
- }
+ struct aws_priority_queue *timeout_queue = &client->operational_state.operations_by_ack_timeout;
- struct aws_linked_list_node *node = aws_linked_list_begin(&client->operational_state.unacked_operations);
- while (node != aws_linked_list_end(&client->operational_state.unacked_operations)) {
- struct aws_mqtt5_operation *operation = AWS_CONTAINER_OF(node, struct aws_mqtt5_operation, node);
- node = aws_linked_list_next(node);
- if (operation->ack_timeout_timepoint_ns < now) {
- /* Timeout for this packet has been reached */
- aws_mqtt5_packet_id_t packet_id = aws_mqtt5_operation_get_packet_id(operation);
-
- switch (operation->packet_type) {
- case AWS_MQTT5_PT_SUBSCRIBE:
- /* SUBSCRIBE has timed out. */
- AWS_LOGF_INFO(
- AWS_LS_MQTT5_CLIENT,
- "id=%p: SUBSCRIBE packet with id:%d has timed out",
- (void *)client,
- packet_id);
- break;
+ bool done = aws_priority_queue_size(timeout_queue) == 0;
+ while (!done) {
+ struct aws_mqtt5_operation **next_operation_by_timeout_ptr = NULL;
+ aws_priority_queue_top(timeout_queue, (void **)&next_operation_by_timeout_ptr);
+ AWS_FATAL_ASSERT(next_operation_by_timeout_ptr != NULL);
+ struct aws_mqtt5_operation *next_operation_by_timeout = *next_operation_by_timeout_ptr;
+ AWS_FATAL_ASSERT(next_operation_by_timeout != NULL);
- case AWS_MQTT5_PT_UNSUBSCRIBE:
- /* UNSUBSCRIBE has timed out. */
- AWS_LOGF_INFO(
- AWS_LS_MQTT5_CLIENT,
- "id=%p: UNSUBSCRIBE packet with id:%d has timed out",
- (void *)client,
- packet_id);
- break;
+ // If the top of the heap hasn't timed out than nothing has
+ if (next_operation_by_timeout->ack_timeout_timepoint_ns > now) {
+ break;
+ }
- case AWS_MQTT5_PT_PUBLISH:
- /* PUBLISH has timed out. */
- AWS_LOGF_INFO(
- AWS_LS_MQTT5_CLIENT,
- "id=%p: PUBLISH packet with id:%d has timed out",
- (void *)client,
- packet_id);
+ /* Ack timeout for this operation has been reached */
+ aws_priority_queue_pop(timeout_queue, &next_operation_by_timeout);
- aws_mqtt5_client_flow_control_state_on_puback(client);
- break;
+ aws_mqtt5_packet_id_t packet_id = aws_mqtt5_operation_get_packet_id(next_operation_by_timeout);
+ AWS_LOGF_INFO(
+ AWS_LS_MQTT5_CLIENT,
+ "id=%p: %s packet with id:%d has timed out",
+ (void *)client,
+ aws_mqtt5_packet_type_to_c_string(next_operation_by_timeout->packet_type),
+ (int)packet_id);
- default:
- /* something is wrong, there should be no other packet type in this linked list */
- break;
- }
+ struct aws_hash_element *elem = NULL;
+ aws_hash_table_find(&client->operational_state.unacked_operations_table, &packet_id, &elem);
- struct aws_hash_element *elem = NULL;
- aws_hash_table_find(&client->operational_state.unacked_operations_table, &packet_id, &elem);
+ if (elem == NULL || elem->value == NULL) {
+ AWS_LOGF_ERROR(
+ AWS_LS_MQTT5_CLIENT, "id=%p: timeout for unknown operation with id %d", (void *)client, (int)packet_id);
+ return;
+ }
- if (elem == NULL || elem->value == NULL) {
- AWS_LOGF_ERROR(
- AWS_LS_MQTT5_CLIENT,
- "id=%p: timeout for unknown operation with id %d",
- (void *)client,
- (int)packet_id);
- return;
- }
+ aws_linked_list_remove(&next_operation_by_timeout->node);
+ aws_hash_table_remove(&client->operational_state.unacked_operations_table, &packet_id, NULL, NULL);
- aws_linked_list_remove(&operation->node);
- aws_hash_table_remove(&client->operational_state.unacked_operations_table, &packet_id, NULL, NULL);
+ s_complete_operation(client, next_operation_by_timeout, AWS_ERROR_MQTT_TIMEOUT, AWS_MQTT5_PT_NONE, NULL);
- s_complete_operation(client, operation, AWS_ERROR_MQTT_TIMEOUT, AWS_MQTT5_PT_NONE, NULL);
- } else {
- break;
- }
+ done = aws_priority_queue_size(timeout_queue) == 0;
}
}
@@ -447,7 +423,11 @@ static uint64_t s_compute_next_service_time_client_mqtt_connect(struct aws_mqtt5
return aws_min_u64(client->next_mqtt_connect_packet_timeout_time, operation_processing_time);
}
-static uint64_t s_min_non_0_64(uint64_t a, uint64_t b) {
+/*
+ * Returns the minimum of two numbers, ignoring zero. Zero is returned only if both are zero. Useful when we're
+ * computing (next service) timepoints and zero means "no timepoint"
+ */
+static uint64_t s_min_non_zero_u64(uint64_t a, uint64_t b) {
if (a == 0) {
return b;
}
@@ -459,6 +439,19 @@ static uint64_t s_min_non_0_64(uint64_t a, uint64_t b) {
return aws_min_u64(a, b);
}
+/*
+ * If there are unacked operations, returns the earliest point in time that one could timeout.
+ */
+static uint64_t s_get_unacked_operation_timeout_for_next_service_time(struct aws_mqtt5_client *client) {
+ if (aws_priority_queue_size(&client->operational_state.operations_by_ack_timeout) > 0) {
+ struct aws_mqtt5_operation **operation = NULL;
+ aws_priority_queue_top(&client->operational_state.operations_by_ack_timeout, (void **)&operation);
+ return (*operation)->ack_timeout_timepoint_ns;
+ }
+
+ return 0;
+}
+
static uint64_t s_compute_next_service_time_client_connected(struct aws_mqtt5_client *client, uint64_t now) {
/* ping and ping timeout */
@@ -467,13 +460,8 @@ static uint64_t s_compute_next_service_time_client_connected(struct aws_mqtt5_cl
next_service_time = aws_min_u64(next_service_time, client->next_ping_timeout_time);
}
- /* unacked operations timeout */
- if (client->config->ack_timeout_seconds != 0 &&
- !aws_linked_list_empty(&client->operational_state.unacked_operations)) {
- struct aws_linked_list_node *node = aws_linked_list_begin(&client->operational_state.unacked_operations);
- struct aws_mqtt5_operation *operation = AWS_CONTAINER_OF(node, struct aws_mqtt5_operation, node);
- next_service_time = aws_min_u64(next_service_time, operation->ack_timeout_timepoint_ns);
- }
+ next_service_time =
+ s_min_non_zero_u64(next_service_time, s_get_unacked_operation_timeout_for_next_service_time(client));
if (client->desired_state != AWS_MCS_CONNECTED) {
next_service_time = now;
@@ -482,29 +470,21 @@ static uint64_t s_compute_next_service_time_client_connected(struct aws_mqtt5_cl
uint64_t operation_processing_time =
s_aws_mqtt5_client_compute_operational_state_service_time(&client->operational_state, now);
- next_service_time = s_min_non_0_64(operation_processing_time, next_service_time);
+ next_service_time = s_min_non_zero_u64(operation_processing_time, next_service_time);
/* reset reconnect delay interval */
- next_service_time = s_min_non_0_64(client->next_reconnect_delay_reset_time_ns, next_service_time);
+ next_service_time = s_min_non_zero_u64(client->next_reconnect_delay_reset_time_ns, next_service_time);
return next_service_time;
}
static uint64_t s_compute_next_service_time_client_clean_disconnect(struct aws_mqtt5_client *client, uint64_t now) {
- uint64_t ack_timeout_time = 0;
-
- /* unacked operations timeout */
- if (client->config->ack_timeout_seconds != 0 &&
- !aws_linked_list_empty(&client->operational_state.unacked_operations)) {
- struct aws_linked_list_node *node = aws_linked_list_begin(&client->operational_state.unacked_operations);
- struct aws_mqtt5_operation *operation = AWS_CONTAINER_OF(node, struct aws_mqtt5_operation, node);
- ack_timeout_time = operation->ack_timeout_timepoint_ns;
- }
+ uint64_t ack_timeout_time = s_get_unacked_operation_timeout_for_next_service_time(client);
uint64_t operation_processing_time =
s_aws_mqtt5_client_compute_operational_state_service_time(&client->operational_state, now);
- return s_min_non_0_64(ack_timeout_time, operation_processing_time);
+ return s_min_non_zero_u64(ack_timeout_time, operation_processing_time);
}
static uint64_t s_compute_next_service_time_client_channel_shutdown(struct aws_mqtt5_client *client, uint64_t now) {
@@ -622,8 +602,10 @@ static void s_aws_mqtt5_client_operational_state_reset(
s_complete_operation_list(client, &client_operational_state->unacked_operations, completion_error_code);
if (is_final) {
+ aws_priority_queue_clean_up(&client_operational_state->operations_by_ack_timeout);
aws_hash_table_clean_up(&client_operational_state->unacked_operations_table);
} else {
+ aws_priority_queue_clear(&client->operational_state.operations_by_ack_timeout);
aws_hash_table_clear(&client_operational_state->unacked_operations_table);
}
}
@@ -715,30 +697,6 @@ static void s_aws_mqtt5_client_shutdown_channel_clean(
aws_mqtt5_operation_disconnect_release(disconnect_op);
}
-struct aws_mqtt5_shutdown_task {
- struct aws_task task;
- struct aws_allocator *allocator;
- int error_code;
- struct aws_mqtt5_client *client;
-};
-
-static void s_mqtt5_client_shutdown_final(int error_code, struct aws_mqtt5_client *client);
-
-static void s_shutdown_task_fn(struct aws_task *task, void *arg, enum aws_task_status status) {
- (void)task;
-
- struct aws_mqtt5_shutdown_task *shutdown_task = arg;
- if (status != AWS_TASK_STATUS_RUN_READY) {
- goto done;
- }
-
- s_mqtt5_client_shutdown_final(shutdown_task->error_code, shutdown_task->client);
-
-done:
-
- aws_mem_release(shutdown_task->allocator, shutdown_task);
-}
-
static void s_mqtt5_client_shutdown_final(int error_code, struct aws_mqtt5_client *client) {
AWS_FATAL_ASSERT(aws_event_loop_thread_is_callers_thread(client->loop));
@@ -782,19 +740,8 @@ static void s_mqtt5_client_shutdown(
error_code = AWS_ERROR_MQTT_UNEXPECTED_HANGUP;
}
- if (aws_event_loop_thread_is_callers_thread(client->loop)) {
- s_mqtt5_client_shutdown_final(error_code, client);
- return;
- }
-
- struct aws_mqtt5_shutdown_task *shutdown_task =
- aws_mem_calloc(client->allocator, 1, sizeof(struct aws_mqtt5_shutdown_task));
-
- aws_task_init(&shutdown_task->task, s_shutdown_task_fn, (void *)shutdown_task, "ShutdownTask");
- shutdown_task->allocator = client->allocator;
- shutdown_task->client = client;
- shutdown_task->error_code = error_code;
- aws_event_loop_schedule_task_now(client->loop, &shutdown_task->task);
+ AWS_FATAL_ASSERT(aws_event_loop_thread_is_callers_thread(client->loop));
+ s_mqtt5_client_shutdown_final(error_code, client);
}
static void s_mqtt5_client_setup(
@@ -939,7 +886,7 @@ void s_websocket_transform_complete_task_fn(struct aws_task *task, void *arg, en
.on_connection_setup = s_on_websocket_setup,
.on_connection_shutdown = s_on_websocket_shutdown,
.requested_event_loop = client->loop,
- };
+ .host_resolution_config = &client->config->host_resolution_override};
if (client->config->http_proxy_config != NULL) {
websocket_options.proxy_options = &client->config->http_proxy_options;
@@ -991,8 +938,6 @@ static void s_websocket_handshake_transform_complete(
task->handshake = handshake_request;
aws_event_loop_schedule_task_now(client->loop, &task->task);
-
- aws_mqtt5_client_release(client);
}
static int s_websocket_connect(struct aws_mqtt5_client *client) {
@@ -1016,7 +961,11 @@ static int s_websocket_connect(struct aws_mqtt5_client *client) {
AWS_LOGF_TRACE(AWS_LS_MQTT5_CLIENT, "id=%p: Transforming websocket handshake request.", (void *)client);
- aws_mqtt5_client_acquire(client);
+ /*
+ * There is no need to inc the client's ref count here since this state (AWS_MCS_CONNECTING) is uninterruptible by
+ * the async destruction process. Only a completion of the chain of connection establishment callbacks can cause
+ * this state to be left by the client.
+ */
client->config->websocket_handshake_transform(
handshake,
client->config->websocket_handshake_transform_user_data,
@@ -1037,6 +986,7 @@ static void s_change_current_state_to_connecting(struct aws_mqtt5_client *client
client->current_state = AWS_MCS_CONNECTING;
client->clean_disconnect_error_code = AWS_ERROR_SUCCESS;
+ client->should_reset_connection = false;
s_aws_mqtt5_client_emit_connecting_lifecycle_event(client);
@@ -1055,6 +1005,7 @@ static void s_change_current_state_to_connecting(struct aws_mqtt5_client *client
channel_options.shutdown_callback = &s_mqtt5_client_shutdown;
channel_options.user_data = client;
channel_options.requested_event_loop = client->loop;
+ channel_options.host_resolution_override_config = &client->config->host_resolution_override;
if (client->config->http_proxy_config == NULL) {
result = (*client->vtable->client_bootstrap_new_socket_channel_fn)(&channel_options);
@@ -1118,7 +1069,11 @@ static void s_reset_ping(struct aws_mqtt5_client *client) {
uint64_t keep_alive_interval_nanos =
aws_timestamp_convert(keep_alive_seconds, AWS_TIMESTAMP_SECS, AWS_TIMESTAMP_NANOS, NULL);
- client->next_ping_time = aws_add_u64_saturating(now, keep_alive_interval_nanos);
+ if (keep_alive_interval_nanos == 0) {
+ client->next_ping_time = UINT64_MAX;
+ } else {
+ client->next_ping_time = aws_add_u64_saturating(now, keep_alive_interval_nanos);
+ }
AWS_LOGF_DEBUG(
AWS_LS_MQTT5_CLIENT, "id=%p: next PINGREQ scheduled for time %" PRIu64, (void *)client, client->next_ping_time);
@@ -1197,13 +1152,18 @@ static void s_change_current_state_to_mqtt_connect(struct aws_mqtt5_client *clie
AWS_FATAL_ASSERT(client->operational_state.current_operation == NULL);
client->current_state = AWS_MCS_MQTT_CONNECT;
+ if (client->should_reset_connection) {
+ s_aws_mqtt5_client_shutdown_channel(client, AWS_ERROR_MQTT_CONNECTION_RESET_FOR_ADAPTER_CONNECT);
+ return;
+ }
+
client->operational_state.pending_write_completion = false;
aws_mqtt5_encoder_reset(&client->encoder);
aws_mqtt5_decoder_reset(&client->decoder);
bool resume_session = s_should_resume_session(client);
- struct aws_mqtt5_packet_connect_view connect_view = client->config->connect.storage_view;
+ struct aws_mqtt5_packet_connect_view connect_view = client->config->connect->storage_view;
connect_view.clean_start = !resume_session;
if (aws_mqtt5_inbound_topic_alias_behavior_type_to_non_default(
@@ -1635,7 +1595,7 @@ static int s_process_read_message(
if (message->message_type != AWS_IO_MESSAGE_APPLICATION_DATA) {
AWS_LOGF_ERROR(AWS_LS_MQTT5_CLIENT, "id=%p: unexpected io message data", (void *)client);
- return AWS_OP_ERR;
+ return aws_raise_error(AWS_ERROR_INVALID_STATE);
}
AWS_LOGF_TRACE(
@@ -2146,15 +2106,11 @@ struct aws_mqtt_change_desired_state_task {
struct aws_mqtt5_operation_disconnect *disconnect_operation;
};
-static void s_change_state_task_fn(struct aws_task *task, void *arg, enum aws_task_status status) {
- (void)task;
-
- struct aws_mqtt_change_desired_state_task *change_state_task = arg;
- struct aws_mqtt5_client *client = change_state_task->client;
- enum aws_mqtt5_client_state desired_state = change_state_task->desired_state;
- if (status != AWS_TASK_STATUS_RUN_READY) {
- goto done;
- }
+void aws_mqtt5_client_change_desired_state(
+ struct aws_mqtt5_client *client,
+ enum aws_mqtt5_client_state desired_state,
+ struct aws_mqtt5_operation_disconnect *disconnect_op) {
+ AWS_FATAL_ASSERT(aws_event_loop_thread_is_callers_thread(client->loop));
if (client->desired_state != desired_state) {
AWS_LOGF_INFO(
@@ -2166,7 +2122,6 @@ static void s_change_state_task_fn(struct aws_task *task, void *arg, enum aws_ta
client->desired_state = desired_state;
- struct aws_mqtt5_operation_disconnect *disconnect_op = change_state_task->disconnect_operation;
if (desired_state == AWS_MCS_STOPPED && disconnect_op != NULL) {
s_aws_mqtt5_client_shutdown_channel_with_disconnect(
client, AWS_ERROR_MQTT5_USER_REQUESTED_STOP, disconnect_op);
@@ -2174,6 +2129,19 @@ static void s_change_state_task_fn(struct aws_task *task, void *arg, enum aws_ta
s_reevaluate_service_task(client);
}
+}
+
+static void s_change_state_task_fn(struct aws_task *task, void *arg, enum aws_task_status status) {
+ (void)task;
+
+ struct aws_mqtt_change_desired_state_task *change_state_task = arg;
+ struct aws_mqtt5_client *client = change_state_task->client;
+ enum aws_mqtt5_client_state desired_state = change_state_task->desired_state;
+ if (status != AWS_TASK_STATUS_RUN_READY) {
+ goto done;
+ }
+
+ aws_mqtt5_client_change_desired_state(client, desired_state, change_state_task->disconnect_operation);
done:
@@ -2297,51 +2265,51 @@ struct aws_mqtt5_submit_operation_task {
struct aws_mqtt5_operation *operation;
};
-static void s_mqtt5_submit_operation_task_fn(struct aws_task *task, void *arg, enum aws_task_status status) {
- (void)task;
-
- int completion_error_code = AWS_ERROR_MQTT5_CLIENT_TERMINATED;
- struct aws_mqtt5_submit_operation_task *submit_operation_task = arg;
+void aws_mqtt5_client_submit_operation_internal(
+ struct aws_mqtt5_client *client,
+ struct aws_mqtt5_operation *operation,
+ bool is_terminated) {
/*
* Take a ref to the operation that represents the client taking ownership
* If we subsequently reject it (task cancel or offline queue policy), then the operation completion
* will undo this ref acquisition.
*/
- aws_mqtt5_operation_acquire(submit_operation_task->operation);
+ aws_mqtt5_operation_acquire(operation);
- if (status != AWS_TASK_STATUS_RUN_READY) {
- goto error;
+ if (is_terminated) {
+ s_complete_operation(NULL, operation, AWS_ERROR_MQTT5_CLIENT_TERMINATED, AWS_MQTT5_PT_NONE, NULL);
+ return;
}
/*
* If we're offline and this operation doesn't meet the requirements of the offline queue retention policy,
* fail it immediately.
*/
- struct aws_mqtt5_client *client = submit_operation_task->client;
- struct aws_mqtt5_operation *operation = submit_operation_task->operation;
if (client->current_state != AWS_MCS_CONNECTED) {
if (!s_aws_mqtt5_operation_satisfies_offline_queue_retention_policy(
operation, client->config->offline_queue_behavior)) {
- completion_error_code = AWS_ERROR_MQTT5_OPERATION_FAILED_DUE_TO_OFFLINE_QUEUE_POLICY;
- goto error;
+ s_complete_operation(
+ NULL, operation, AWS_ERROR_MQTT5_OPERATION_FAILED_DUE_TO_OFFLINE_QUEUE_POLICY, AWS_MQTT5_PT_NONE, NULL);
+ return;
}
}
/* newly-submitted operations must have a 0 packet id */
- aws_mqtt5_operation_set_packet_id(submit_operation_task->operation, 0);
-
- s_enqueue_operation_back(submit_operation_task->client, submit_operation_task->operation);
- aws_mqtt5_client_statistics_change_operation_statistic_state(
- submit_operation_task->client, submit_operation_task->operation, AWS_MQTT5_OSS_INCOMPLETE);
+ aws_mqtt5_operation_set_packet_id(operation, 0);
- goto done;
+ s_enqueue_operation_back(client, operation);
+ aws_mqtt5_client_statistics_change_operation_statistic_state(client, operation, AWS_MQTT5_OSS_INCOMPLETE);
+}
-error:
+static void s_mqtt5_submit_operation_task_fn(struct aws_task *task, void *arg, enum aws_task_status status) {
+ (void)task;
- s_complete_operation(NULL, submit_operation_task->operation, completion_error_code, AWS_MQTT5_PT_NONE, NULL);
+ struct aws_mqtt5_submit_operation_task *submit_operation_task = arg;
+ struct aws_mqtt5_client *client = submit_operation_task->client;
+ struct aws_mqtt5_operation *operation = submit_operation_task->operation;
-done:
+ aws_mqtt5_client_submit_operation_internal(client, operation, status != AWS_TASK_STATUS_RUN_READY);
aws_mqtt5_operation_release(submit_operation_task->operation);
aws_mqtt5_client_release(submit_operation_task->client);
@@ -2515,6 +2483,25 @@ int aws_mqtt5_operation_bind_packet_id(
return AWS_OP_ERR;
}
+/*
+ * Priority queue comparison function for ack timeout processing
+ */
+static int s_compare_operation_timeouts(const void *a, const void *b) {
+ const struct aws_mqtt5_operation **operation_a_ptr = (void *)a;
+ const struct aws_mqtt5_operation *operation_a = *operation_a_ptr;
+
+ const struct aws_mqtt5_operation **operation_b_ptr = (void *)b;
+ const struct aws_mqtt5_operation *operation_b = *operation_b_ptr;
+
+ if (operation_a->ack_timeout_timepoint_ns < operation_b->ack_timeout_timepoint_ns) {
+ return -1;
+ } else if (operation_a->ack_timeout_timepoint_ns > operation_b->ack_timeout_timepoint_ns) {
+ return 1;
+ } else {
+ return 0;
+ }
+}
+
int aws_mqtt5_client_operational_state_init(
struct aws_mqtt5_client_operational_state *client_operational_state,
struct aws_allocator *allocator,
@@ -2527,14 +2514,23 @@ int aws_mqtt5_client_operational_state_init(
if (aws_hash_table_init(
&client_operational_state->unacked_operations_table,
allocator,
- sizeof(struct aws_mqtt5_operation *),
- s_hash_uint16_t,
- s_uint16_t_eq,
+ DEFAULT_MQTT5_OPERATION_TABLE_SIZE,
+ aws_mqtt_hash_uint16_t,
+ aws_mqtt_compare_uint16_t_eq,
NULL,
NULL)) {
return AWS_OP_ERR;
}
+ if (aws_priority_queue_init_dynamic(
+ &client_operational_state->operations_by_ack_timeout,
+ allocator,
+ 100,
+ sizeof(struct aws_mqtt5_operation *),
+ s_compare_operation_timeouts)) {
+ return AWS_OP_ERR;
+ }
+
client_operational_state->next_mqtt_packet_id = 1;
client_operational_state->current_operation = NULL;
client_operational_state->client = client;
@@ -2649,6 +2645,7 @@ void aws_mqtt5_client_on_disconnection_update_operational_state(struct aws_mqtt5
client, &operations_to_fail, AWS_ERROR_MQTT5_OPERATION_FAILED_DUE_TO_OFFLINE_QUEUE_POLICY);
aws_hash_table_clear(&client->operational_state.unacked_operations_table);
+ aws_priority_queue_clear(&client->operational_state.operations_by_ack_timeout);
/*
* Prevents inbound resolution on the highly unlikely, illegal server behavior of sending a PUBLISH before
@@ -2947,7 +2944,20 @@ static void s_on_pingreq_send(struct aws_mqtt5_client *client) {
uint64_t now = client->vtable->get_current_time_fn();
uint64_t ping_timeout_nanos =
aws_timestamp_convert(client->config->ping_timeout_ms, AWS_TIMESTAMP_MILLIS, AWS_TIMESTAMP_NANOS, NULL);
- client->next_ping_timeout_time = aws_add_u64_saturating(now, ping_timeout_nanos);
+ uint64_t half_keep_alive_nanos =
+ aws_timestamp_convert(
+ client->negotiated_settings.server_keep_alive, AWS_TIMESTAMP_SECS, AWS_TIMESTAMP_NANOS, NULL) /
+ 2;
+
+ uint64_t connection_ping_timeout = ping_timeout_nanos;
+ if (connection_ping_timeout == 0 || connection_ping_timeout > half_keep_alive_nanos) {
+ connection_ping_timeout = half_keep_alive_nanos;
+ }
+
+ AWS_LOGF_DEBUG(
+ AWS_LS_MQTT5_CLIENT, "id=%p: dynamic ping timeout: %" PRIu64 " ns", (void *)client, connection_ping_timeout);
+
+ client->next_ping_timeout_time = aws_add_u64_saturating(now, connection_ping_timeout);
}
static int s_apply_throughput_flow_control(struct aws_mqtt5_client *client) {
@@ -3014,14 +3024,18 @@ int aws_mqtt5_client_service_operational_state(struct aws_mqtt5_client_operation
struct aws_mqtt5_operation *next_operation = NULL;
while (!aws_linked_list_empty(&client_operational_state->queued_operations)) {
struct aws_linked_list_node *next_operation_node =
- aws_linked_list_pop_front(&client_operational_state->queued_operations);
+ aws_linked_list_front(&client_operational_state->queued_operations);
struct aws_mqtt5_operation *operation =
AWS_CONTAINER_OF(next_operation_node, struct aws_mqtt5_operation, node);
+ /* If this is a publish and we're throttled, just quit out of the loop. */
if (s_apply_publish_tps_flow_control(client, operation)) {
break;
}
+ /* Wait until flow control has passed before actually dequeuing the operation. */
+ aws_linked_list_pop_front(&client_operational_state->queued_operations);
+
if (!aws_mqtt5_operation_validate_vs_connection_settings(operation, client)) {
next_operation = operation;
break;
@@ -3079,10 +3093,24 @@ int aws_mqtt5_client_service_operational_state(struct aws_mqtt5_client_operation
break;
}
- if (client->config->ack_timeout_seconds != 0) {
+ uint32_t ack_timeout_seconds = aws_mqtt5_operation_get_ack_timeout_override(current_operation);
+ if (ack_timeout_seconds == 0) {
+ ack_timeout_seconds = client->config->ack_timeout_seconds;
+ }
+
+ if (ack_timeout_seconds > 0) {
current_operation->ack_timeout_timepoint_ns =
- now + aws_timestamp_convert(
- client->config->ack_timeout_seconds, AWS_TIMESTAMP_SECS, AWS_TIMESTAMP_NANOS, NULL);
+ now + aws_timestamp_convert(ack_timeout_seconds, AWS_TIMESTAMP_SECS, AWS_TIMESTAMP_NANOS, NULL);
+ } else {
+ current_operation->ack_timeout_timepoint_ns = UINT64_MAX;
+ }
+
+ if (aws_priority_queue_push_ref(
+ &client_operational_state->operations_by_ack_timeout,
+ (void *)&current_operation,
+ &current_operation->priority_queue_node)) {
+ operational_error_code = aws_last_error();
+ break;
}
aws_linked_list_push_back(&client_operational_state->unacked_operations, &current_operation->node);
@@ -3342,3 +3370,25 @@ void aws_mqtt5_client_get_stats(struct aws_mqtt5_client *client, struct aws_mqtt
stats->unacked_operation_size =
(uint64_t)aws_atomic_load_int(&client->operation_statistics_impl.unacked_operation_size_atomic);
}
+
+bool aws_mqtt5_client_reset_connection(struct aws_mqtt5_client *client) {
+ AWS_FATAL_ASSERT(aws_event_loop_thread_is_callers_thread(client->loop));
+
+ client->current_reconnect_delay_ms = client->config->min_reconnect_delay_ms;
+
+ switch (client->current_state) {
+ case AWS_MCS_MQTT_CONNECT:
+ case AWS_MCS_CONNECTED:
+ s_aws_mqtt5_client_shutdown_channel(client, AWS_ERROR_MQTT_CONNECTION_RESET_FOR_ADAPTER_CONNECT);
+ return true;
+
+ case AWS_MCS_CONNECTING:
+ client->should_reset_connection = true;
+ return true;
+
+ default:
+ break;
+ }
+
+ return false;
+}
diff --git a/contrib/restricted/aws/aws-c-mqtt/source/v5/mqtt5_encoder.c b/contrib/restricted/aws/aws-c-mqtt/source/v5/mqtt5_encoder.c
index b9a3ec56ec0..fe1ab3aba07 100644
--- a/contrib/restricted/aws/aws-c-mqtt/source/v5/mqtt5_encoder.c
+++ b/contrib/restricted/aws/aws-c-mqtt/source/v5/mqtt5_encoder.c
@@ -800,7 +800,25 @@ static int s_aws_mqtt5_encoder_begin_publish(struct aws_mqtt5_encoder *encoder,
local_publish_view.topic = outbound_topic;
if (outbound_topic_alias != 0) {
+ AWS_LOGF_DEBUG(
+ AWS_LS_MQTT5_GENERAL,
+ "(%p) mqtt5 client encoder - PUBLISH packet using topic alias value %" PRIu16,
+ (void *)encoder->config.client,
+ outbound_topic_alias);
+ if (outbound_topic.len == 0) {
+ AWS_LOGF_DEBUG(
+ AWS_LS_MQTT5_GENERAL,
+ "(%p) mqtt5 client encoder - PUBLISH packet dropping topic field for previously established alias",
+ (void *)encoder->config.client);
+ }
local_publish_view.topic_alias = &outbound_topic_alias;
+ } else {
+ AWS_FATAL_ASSERT(local_publish_view.topic.len > 0);
+ AWS_LOGF_DEBUG(
+ AWS_LS_MQTT5_GENERAL,
+ "(%p) mqtt5 client encoder - PUBLISH packet not using a topic alias",
+ (void *)encoder->config.client);
+ local_publish_view.topic_alias = NULL;
}
}
diff --git a/contrib/restricted/aws/aws-c-mqtt/source/v5/mqtt5_options_storage.c b/contrib/restricted/aws/aws-c-mqtt/source/v5/mqtt5_options_storage.c
index b8b566e910b..6510c6ab21f 100644
--- a/contrib/restricted/aws/aws-c-mqtt/source/v5/mqtt5_options_storage.c
+++ b/contrib/restricted/aws/aws-c-mqtt/source/v5/mqtt5_options_storage.c
@@ -80,19 +80,6 @@ size_t aws_mqtt5_user_property_set_size(const struct aws_mqtt5_user_property_set
return aws_array_list_length(&property_set->properties);
}
-int aws_mqtt5_user_property_set_get_property(
- const struct aws_mqtt5_user_property_set *property_set,
- size_t index,
- struct aws_mqtt5_user_property *property_out) {
- return aws_array_list_get_at(&property_set->properties, property_out, index);
-}
-
-int aws_mqtt5_user_property_set_add_stored_property(
- struct aws_mqtt5_user_property_set *property_set,
- struct aws_mqtt5_user_property *property) {
- return aws_array_list_push_back(&property_set->properties, property);
-}
-
static void s_aws_mqtt5_user_property_set_log(
struct aws_logger *log_handle,
const struct aws_mqtt5_user_property *properties,
@@ -185,7 +172,7 @@ static int s_aws_mqtt5_user_property_set_validate(
return aws_raise_error(AWS_ERROR_MQTT5_USER_PROPERTY_VALIDATION);
}
- if (aws_mqtt5_validate_utf8_text(property->name)) {
+ if (aws_mqtt_validate_utf8_text(property->name)) {
AWS_LOGF_ERROR(
AWS_LS_MQTT5_GENERAL, "id=%p: %s - user property #%zu name not valid UTF8", log_context, log_prefix, i);
return aws_raise_error(AWS_ERROR_MQTT5_USER_PROPERTY_VALIDATION);
@@ -200,7 +187,7 @@ static int s_aws_mqtt5_user_property_set_validate(
property->value.len);
return aws_raise_error(AWS_ERROR_MQTT5_USER_PROPERTY_VALIDATION);
}
- if (aws_mqtt5_validate_utf8_text(property->value)) {
+ if (aws_mqtt_validate_utf8_text(property->value)) {
AWS_LOGF_ERROR(
AWS_LS_MQTT5_GENERAL,
"id=%p: %s - user property #%zu value not valid UTF8",
@@ -322,11 +309,20 @@ int aws_mqtt5_operation_validate_vs_connection_settings(
return AWS_OP_SUCCESS;
}
+uint32_t aws_mqtt5_operation_get_ack_timeout_override(const struct aws_mqtt5_operation *operation) {
+ if (operation->vtable->aws_mqtt5_operation_get_ack_timeout_override_fn != NULL) {
+ return (*operation->vtable->aws_mqtt5_operation_get_ack_timeout_override_fn)(operation);
+ }
+
+ return 0;
+}
+
static struct aws_mqtt5_operation_vtable s_empty_operation_vtable = {
.aws_mqtt5_operation_completion_fn = NULL,
.aws_mqtt5_operation_set_packet_id_fn = NULL,
.aws_mqtt5_operation_get_packet_id_address_fn = NULL,
.aws_mqtt5_operation_validate_vs_connection_settings_fn = NULL,
+ .aws_mqtt5_operation_get_ack_timeout_override_fn = NULL,
};
/*********************************************************************************************************************
@@ -345,7 +341,7 @@ int aws_mqtt5_packet_connect_view_validate(const struct aws_mqtt5_packet_connect
return aws_raise_error(AWS_ERROR_MQTT5_CONNECT_OPTIONS_VALIDATION);
}
- if (aws_mqtt5_validate_utf8_text(connect_options->client_id)) {
+ if (aws_mqtt_validate_utf8_text(connect_options->client_id)) {
AWS_LOGF_ERROR(
AWS_LS_MQTT5_GENERAL,
"id=%p: aws_mqtt5_packet_connect_view - client id not valid UTF-8",
@@ -362,7 +358,7 @@ int aws_mqtt5_packet_connect_view_validate(const struct aws_mqtt5_packet_connect
return aws_raise_error(AWS_ERROR_MQTT5_CONNECT_OPTIONS_VALIDATION);
}
- if (aws_mqtt5_validate_utf8_text(*connect_options->username)) {
+ if (aws_mqtt_validate_utf8_text(*connect_options->username)) {
AWS_LOGF_ERROR(
AWS_LS_MQTT5_GENERAL,
"id=%p: aws_mqtt5_packet_connect_view - username not valid UTF-8",
@@ -833,6 +829,7 @@ struct aws_mqtt5_operation_connect *aws_mqtt5_operation_connect_new(
connect_op->base.vtable = &s_empty_operation_vtable;
connect_op->base.packet_type = AWS_MQTT5_PT_CONNECT;
aws_ref_count_init(&connect_op->base.ref_count, connect_op, s_destroy_operation_connect);
+ aws_priority_queue_node_init(&connect_op->base.priority_queue_node);
connect_op->base.impl = connect_op;
if (aws_mqtt5_packet_connect_storage_init(&connect_op->options_storage, allocator, connect_options)) {
@@ -1272,7 +1269,7 @@ int aws_mqtt5_packet_disconnect_view_validate(const struct aws_mqtt5_packet_disc
return aws_raise_error(AWS_ERROR_MQTT5_DISCONNECT_OPTIONS_VALIDATION);
}
- if (aws_mqtt5_validate_utf8_text(*disconnect_view->reason_string)) {
+ if (aws_mqtt_validate_utf8_text(*disconnect_view->reason_string)) {
AWS_LOGF_ERROR(
AWS_LS_MQTT5_GENERAL,
"id=%p: aws_mqtt5_packet_disconnect_view - reason string not valid UTF-8",
@@ -1313,7 +1310,7 @@ static int s_aws_mqtt5_packet_disconnect_view_validate_vs_connection_settings(
* cannot set a non-zero value here if you sent a 0-value or no value in the CONNECT (presumably allows
* the server to skip tracking session state, and we can't undo that now)
*/
- const uint32_t *session_expiry_ptr = client->config->connect.storage_view.session_expiry_interval_seconds;
+ const uint32_t *session_expiry_ptr = client->config->connect->storage_view.session_expiry_interval_seconds;
if (*disconnect_view->session_expiry_interval_seconds > 0 &&
(session_expiry_ptr == NULL || *session_expiry_ptr == 0)) {
AWS_LOGF_ERROR(
@@ -1515,6 +1512,7 @@ static struct aws_mqtt5_operation_vtable s_disconnect_operation_vtable = {
.aws_mqtt5_operation_get_packet_id_address_fn = NULL,
.aws_mqtt5_operation_validate_vs_connection_settings_fn =
s_aws_mqtt5_packet_disconnect_view_validate_vs_connection_settings,
+ .aws_mqtt5_operation_get_ack_timeout_override_fn = NULL,
};
struct aws_mqtt5_operation_disconnect *aws_mqtt5_operation_disconnect_new(
@@ -1538,6 +1536,7 @@ struct aws_mqtt5_operation_disconnect *aws_mqtt5_operation_disconnect_new(
disconnect_op->base.vtable = &s_disconnect_operation_vtable;
disconnect_op->base.packet_type = AWS_MQTT5_PT_DISCONNECT;
aws_ref_count_init(&disconnect_op->base.ref_count, disconnect_op, s_destroy_operation_disconnect);
+ aws_priority_queue_node_init(&disconnect_op->base.priority_queue_node);
disconnect_op->base.impl = disconnect_op;
if (aws_mqtt5_packet_disconnect_storage_init(&disconnect_op->options_storage, allocator, disconnect_options)) {
@@ -1604,7 +1603,7 @@ int aws_mqtt5_packet_publish_view_validate(const struct aws_mqtt5_packet_publish
AWS_LOGF_ERROR(
AWS_LS_MQTT5_GENERAL, "id=%p: aws_mqtt5_packet_publish_view - missing topic", (void *)publish_view);
return aws_raise_error(AWS_ERROR_MQTT5_PUBLISH_OPTIONS_VALIDATION);
- } else if (aws_mqtt5_validate_utf8_text(publish_view->topic)) {
+ } else if (aws_mqtt_validate_utf8_text(publish_view->topic)) {
AWS_LOGF_ERROR(
AWS_LS_MQTT5_GENERAL, "id=%p: aws_mqtt5_packet_publish_view - topic not valid UTF-8", (void *)publish_view);
return aws_raise_error(AWS_ERROR_MQTT5_PUBLISH_OPTIONS_VALIDATION);
@@ -1639,7 +1638,7 @@ int aws_mqtt5_packet_publish_view_validate(const struct aws_mqtt5_packet_publish
// Make sure the payload data is UTF-8 if the payload_format set to UTF8
if (*publish_view->payload_format == AWS_MQTT5_PFI_UTF8) {
- if (aws_mqtt5_validate_utf8_text(publish_view->payload)) {
+ if (aws_mqtt_validate_utf8_text(publish_view->payload)) {
AWS_LOGF_ERROR(
AWS_LS_MQTT5_GENERAL,
"id=%p: aws_mqtt5_packet_publish_view - payload value is not valid UTF-8 while payload format "
@@ -1659,7 +1658,7 @@ int aws_mqtt5_packet_publish_view_validate(const struct aws_mqtt5_packet_publish
return aws_raise_error(AWS_ERROR_MQTT5_PUBLISH_OPTIONS_VALIDATION);
}
- if (aws_mqtt5_validate_utf8_text(*publish_view->response_topic)) {
+ if (aws_mqtt_validate_utf8_text(*publish_view->response_topic)) {
AWS_LOGF_ERROR(
AWS_LS_MQTT5_GENERAL,
"id=%p: aws_mqtt5_packet_publish_view - response topic not valid UTF-8",
@@ -1705,7 +1704,7 @@ int aws_mqtt5_packet_publish_view_validate(const struct aws_mqtt5_packet_publish
return aws_raise_error(AWS_ERROR_MQTT5_PUBLISH_OPTIONS_VALIDATION);
}
- if (aws_mqtt5_validate_utf8_text(*publish_view->content_type)) {
+ if (aws_mqtt_validate_utf8_text(*publish_view->content_type)) {
AWS_LOGF_ERROR(
AWS_LS_MQTT5_GENERAL,
"id=%p: aws_mqtt5_packet_publish_view - content type not valid UTF-8",
@@ -1725,19 +1724,6 @@ int aws_mqtt5_packet_publish_view_validate(const struct aws_mqtt5_packet_publish
return AWS_OP_SUCCESS;
}
-int aws_mqtt5_packet_publish_view_validate_vs_iot_core(const struct aws_mqtt5_packet_publish_view *publish_view) {
- if (!aws_mqtt_is_valid_topic_for_iot_core(publish_view->topic)) {
- AWS_LOGF_ERROR(
- AWS_LS_MQTT5_GENERAL,
- "id=%p: aws_mqtt5_packet_publish_view - topic not valid for AWS Iot Core limits: \"" PRInSTR "\"",
- (void *)publish_view,
- AWS_BYTE_CURSOR_PRI(publish_view->topic));
- return AWS_OP_ERR;
- }
-
- return AWS_OP_SUCCESS;
-}
-
static int s_aws_mqtt5_packet_publish_view_validate_vs_connection_settings(
const void *packet_view,
const struct aws_mqtt5_client *client) {
@@ -1757,30 +1743,6 @@ static int s_aws_mqtt5_packet_publish_view_validate_vs_connection_settings(
return aws_raise_error(AWS_ERROR_MQTT5_PUBLISH_OPTIONS_VALIDATION);
}
- if (publish_view->topic_alias != NULL) {
- const struct aws_mqtt5_client_options_storage *client_options = client->config;
- if (client_options->topic_aliasing_options.outbound_topic_alias_behavior != AWS_MQTT5_COTABT_USER) {
- AWS_LOGF_ERROR(
- AWS_LS_MQTT5_GENERAL,
- "id=%p: aws_mqtt5_packet_publish_view - topic alias set but outbound topic alias behavior has not "
- "been set to user controlled",
- (void *)publish_view);
- return aws_raise_error(AWS_ERROR_MQTT5_PUBLISH_OPTIONS_VALIDATION);
- }
-
- if (*publish_view->topic_alias > settings->topic_alias_maximum_to_server) {
- AWS_LOGF_ERROR(
- AWS_LS_MQTT5_GENERAL,
- "id=%p: aws_mqtt5_packet_publish_view - outbound topic alias (%d) exceeds server's topic alias "
- "maximum "
- "(%d)",
- (void *)publish_view,
- (int)(*publish_view->topic_alias),
- (int)settings->topic_alias_maximum_to_server);
- return aws_raise_error(AWS_ERROR_MQTT5_PUBLISH_OPTIONS_VALIDATION);
- }
- }
-
if (publish_view->retain && settings->retain_available == false) {
AWS_LOGF_ERROR(
AWS_LS_MQTT5_GENERAL,
@@ -2089,13 +2051,18 @@ static aws_mqtt5_packet_id_t *s_aws_mqtt5_operation_publish_get_packet_id_addres
return &publish_op->options_storage.storage_view.packet_id;
}
+static uint32_t s_aws_mqtt5_operation_publish_get_ack_timeout_override(const struct aws_mqtt5_operation *operation) {
+ struct aws_mqtt5_operation_publish *publish_op = operation->impl;
+ return publish_op->completion_options.ack_timeout_seconds_override;
+}
+
static struct aws_mqtt5_operation_vtable s_publish_operation_vtable = {
.aws_mqtt5_operation_completion_fn = s_aws_mqtt5_operation_publish_complete,
.aws_mqtt5_operation_set_packet_id_fn = s_aws_mqtt5_operation_publish_set_packet_id,
.aws_mqtt5_operation_get_packet_id_address_fn = s_aws_mqtt5_operation_publish_get_packet_id_address,
.aws_mqtt5_operation_validate_vs_connection_settings_fn =
s_aws_mqtt5_packet_publish_view_validate_vs_connection_settings,
-};
+ .aws_mqtt5_operation_get_ack_timeout_override_fn = s_aws_mqtt5_operation_publish_get_ack_timeout_override};
static void s_destroy_operation_publish(void *object) {
if (object == NULL) {
@@ -2114,6 +2081,7 @@ struct aws_mqtt5_operation_publish *aws_mqtt5_operation_publish_new(
const struct aws_mqtt5_client *client,
const struct aws_mqtt5_packet_publish_view *publish_options,
const struct aws_mqtt5_publish_completion_options *completion_options) {
+ (void)client;
AWS_PRECONDITION(allocator != NULL);
AWS_PRECONDITION(publish_options != NULL);
@@ -2130,12 +2098,6 @@ struct aws_mqtt5_operation_publish *aws_mqtt5_operation_publish_new(
return NULL;
}
- if (client != NULL && client->config->extended_validation_and_flow_control_options != AWS_MQTT5_EVAFCO_NONE) {
- if (aws_mqtt5_packet_publish_view_validate_vs_iot_core(publish_options)) {
- return NULL;
- }
- }
-
struct aws_mqtt5_operation_publish *publish_op =
aws_mem_calloc(allocator, 1, sizeof(struct aws_mqtt5_operation_publish));
if (publish_op == NULL) {
@@ -2146,6 +2108,7 @@ struct aws_mqtt5_operation_publish *aws_mqtt5_operation_publish_new(
publish_op->base.vtable = &s_publish_operation_vtable;
publish_op->base.packet_type = AWS_MQTT5_PT_PUBLISH;
aws_ref_count_init(&publish_op->base.ref_count, publish_op, s_destroy_operation_publish);
+ aws_priority_queue_node_init(&publish_op->base.priority_queue_node);
publish_op->base.impl = publish_op;
if (aws_mqtt5_packet_publish_storage_init(&publish_op->options_storage, allocator, publish_options)) {
@@ -2316,6 +2279,7 @@ struct aws_mqtt5_operation_puback *aws_mqtt5_operation_puback_new(
puback_op->base.vtable = &s_empty_operation_vtable;
puback_op->base.packet_type = AWS_MQTT5_PT_PUBACK;
aws_ref_count_init(&puback_op->base.ref_count, puback_op, s_destroy_operation_puback);
+ aws_priority_queue_node_init(&puback_op->base.priority_queue_node);
puback_op->base.impl = puback_op;
if (aws_mqtt5_packet_puback_storage_init(&puback_op->options_storage, allocator, puback_options)) {
@@ -2363,7 +2327,7 @@ int aws_mqtt5_packet_unsubscribe_view_validate(const struct aws_mqtt5_packet_uns
for (size_t i = 0; i < unsubscribe_view->topic_filter_count; ++i) {
const struct aws_byte_cursor *topic_filter = &unsubscribe_view->topic_filters[i];
- if (aws_mqtt5_validate_utf8_text(*topic_filter)) {
+ if (aws_mqtt_validate_utf8_text(*topic_filter)) {
AWS_LOGF_ERROR(
AWS_LS_MQTT5_GENERAL,
"id=%p: aws_mqtt5_packet_unsubscribe_view - topic filter not valid UTF-8: \"" PRInSTR "\"",
@@ -2392,25 +2356,6 @@ int aws_mqtt5_packet_unsubscribe_view_validate(const struct aws_mqtt5_packet_uns
return AWS_OP_SUCCESS;
}
-AWS_MQTT_API int aws_mqtt5_packet_unsubscribe_view_validate_vs_iot_core(
- const struct aws_mqtt5_packet_unsubscribe_view *unsubscribe_view) {
-
- for (size_t i = 0; i < unsubscribe_view->topic_filter_count; ++i) {
- const struct aws_byte_cursor *topic_filter = &unsubscribe_view->topic_filters[i];
- if (!aws_mqtt_is_valid_topic_filter_for_iot_core(*topic_filter)) {
- AWS_LOGF_ERROR(
- AWS_LS_MQTT5_GENERAL,
- "id=%p: aws_mqtt5_packet_unsubscribe_view - topic filter not valid for AWS Iot Core limits: \"" PRInSTR
- "\"",
- (void *)unsubscribe_view,
- AWS_BYTE_CURSOR_PRI(*topic_filter));
- return aws_raise_error(AWS_ERROR_MQTT5_UNSUBSCRIBE_OPTIONS_VALIDATION);
- }
- }
-
- return AWS_OP_SUCCESS;
-}
-
void aws_mqtt5_packet_unsubscribe_view_log(
const struct aws_mqtt5_packet_unsubscribe_view *unsubscribe_view,
enum aws_log_level level) {
@@ -2573,11 +2518,18 @@ static aws_mqtt5_packet_id_t *s_aws_mqtt5_operation_unsubscribe_get_packet_id_ad
return &unsubscribe_op->options_storage.storage_view.packet_id;
}
+static uint32_t s_aws_mqtt5_operation_unsubscribe_get_ack_timeout_override(
+ const struct aws_mqtt5_operation *operation) {
+ struct aws_mqtt5_operation_unsubscribe *unsubscribe_op = operation->impl;
+ return unsubscribe_op->completion_options.ack_timeout_seconds_override;
+}
+
static struct aws_mqtt5_operation_vtable s_unsubscribe_operation_vtable = {
.aws_mqtt5_operation_completion_fn = s_aws_mqtt5_operation_unsubscribe_complete,
.aws_mqtt5_operation_set_packet_id_fn = s_aws_mqtt5_operation_unsubscribe_set_packet_id,
.aws_mqtt5_operation_get_packet_id_address_fn = s_aws_mqtt5_operation_unsubscribe_get_packet_id_address,
.aws_mqtt5_operation_validate_vs_connection_settings_fn = NULL,
+ .aws_mqtt5_operation_get_ack_timeout_override_fn = s_aws_mqtt5_operation_unsubscribe_get_ack_timeout_override,
};
static void s_destroy_operation_unsubscribe(void *object) {
@@ -2597,6 +2549,7 @@ struct aws_mqtt5_operation_unsubscribe *aws_mqtt5_operation_unsubscribe_new(
const struct aws_mqtt5_client *client,
const struct aws_mqtt5_packet_unsubscribe_view *unsubscribe_options,
const struct aws_mqtt5_unsubscribe_completion_options *completion_options) {
+ (void)client;
AWS_PRECONDITION(allocator != NULL);
AWS_PRECONDITION(unsubscribe_options != NULL);
@@ -2613,12 +2566,6 @@ struct aws_mqtt5_operation_unsubscribe *aws_mqtt5_operation_unsubscribe_new(
return NULL;
}
- if (client != NULL && client->config->extended_validation_and_flow_control_options != AWS_MQTT5_EVAFCO_NONE) {
- if (aws_mqtt5_packet_unsubscribe_view_validate_vs_iot_core(unsubscribe_options)) {
- return NULL;
- }
- }
-
struct aws_mqtt5_operation_unsubscribe *unsubscribe_op =
aws_mem_calloc(allocator, 1, sizeof(struct aws_mqtt5_operation_unsubscribe));
if (unsubscribe_op == NULL) {
@@ -2629,6 +2576,7 @@ struct aws_mqtt5_operation_unsubscribe *aws_mqtt5_operation_unsubscribe_new(
unsubscribe_op->base.vtable = &s_unsubscribe_operation_vtable;
unsubscribe_op->base.packet_type = AWS_MQTT5_PT_UNSUBSCRIBE;
aws_ref_count_init(&unsubscribe_op->base.ref_count, unsubscribe_op, s_destroy_operation_unsubscribe);
+ aws_priority_queue_node_init(&unsubscribe_op->base.priority_queue_node);
unsubscribe_op->base.impl = unsubscribe_op;
if (aws_mqtt5_packet_unsubscribe_storage_init(&unsubscribe_op->options_storage, allocator, unsubscribe_options)) {
@@ -2658,7 +2606,7 @@ static int s_aws_mqtt5_validate_subscription(
const struct aws_mqtt5_subscription_view *subscription,
void *log_context) {
- if (aws_mqtt5_validate_utf8_text(subscription->topic_filter)) {
+ if (aws_mqtt_validate_utf8_text(subscription->topic_filter)) {
AWS_LOGF_ERROR(
AWS_LS_MQTT5_GENERAL,
"id=%p: aws_mqtt5_packet_subscribe_view - topic filter \"" PRInSTR "\" not valid UTF-8 in subscription",
@@ -2774,37 +2722,6 @@ int aws_mqtt5_packet_subscribe_view_validate(const struct aws_mqtt5_packet_subsc
return AWS_OP_SUCCESS;
}
-AWS_MQTT_API int aws_mqtt5_packet_subscribe_view_validate_vs_iot_core(
- const struct aws_mqtt5_packet_subscribe_view *subscribe_view) {
-
- if (subscribe_view->subscription_count > AWS_IOT_CORE_MAXIMUM_SUSBCRIPTIONS_PER_SUBSCRIBE) {
- AWS_LOGF_ERROR(
- AWS_LS_MQTT5_GENERAL,
- "id=%p: aws_mqtt5_packet_subscribe_view - number of subscriptions (%zu) exceeds default AWS IoT Core limit "
- "(%d)",
- (void *)subscribe_view,
- subscribe_view->subscription_count,
- (int)AWS_IOT_CORE_MAXIMUM_SUSBCRIPTIONS_PER_SUBSCRIBE);
- return AWS_OP_ERR;
- }
-
- for (size_t i = 0; i < subscribe_view->subscription_count; ++i) {
- const struct aws_mqtt5_subscription_view *subscription = &subscribe_view->subscriptions[i];
- const struct aws_byte_cursor *topic_filter = &subscription->topic_filter;
- if (!aws_mqtt_is_valid_topic_filter_for_iot_core(*topic_filter)) {
- AWS_LOGF_ERROR(
- AWS_LS_MQTT5_GENERAL,
- "id=%p: aws_mqtt5_packet_subscribe_view - topic filter not valid for AWS Iot Core limits: \"" PRInSTR
- "\"",
- (void *)subscribe_view,
- AWS_BYTE_CURSOR_PRI(*topic_filter));
- return aws_raise_error(AWS_ERROR_MQTT5_UNSUBSCRIBE_OPTIONS_VALIDATION);
- }
- }
-
- return AWS_OP_SUCCESS;
-}
-
void aws_mqtt5_packet_subscribe_view_log(
const struct aws_mqtt5_packet_subscribe_view *subscribe_view,
enum aws_log_level level) {
@@ -2992,11 +2909,17 @@ static aws_mqtt5_packet_id_t *s_aws_mqtt5_operation_subscribe_get_packet_id_addr
return &subscribe_op->options_storage.storage_view.packet_id;
}
+static uint32_t s_aws_mqtt5_operation_subscribe_get_ack_timeout_override(const struct aws_mqtt5_operation *operation) {
+ struct aws_mqtt5_operation_subscribe *subscribe_op = operation->impl;
+ return subscribe_op->completion_options.ack_timeout_seconds_override;
+}
+
static struct aws_mqtt5_operation_vtable s_subscribe_operation_vtable = {
.aws_mqtt5_operation_completion_fn = s_aws_mqtt5_operation_subscribe_complete,
.aws_mqtt5_operation_set_packet_id_fn = s_aws_mqtt5_operation_subscribe_set_packet_id,
.aws_mqtt5_operation_get_packet_id_address_fn = s_aws_mqtt5_operation_subscribe_get_packet_id_address,
.aws_mqtt5_operation_validate_vs_connection_settings_fn = NULL,
+ .aws_mqtt5_operation_get_ack_timeout_override_fn = s_aws_mqtt5_operation_subscribe_get_ack_timeout_override,
};
static void s_destroy_operation_subscribe(void *object) {
@@ -3016,6 +2939,7 @@ struct aws_mqtt5_operation_subscribe *aws_mqtt5_operation_subscribe_new(
const struct aws_mqtt5_client *client,
const struct aws_mqtt5_packet_subscribe_view *subscribe_options,
const struct aws_mqtt5_subscribe_completion_options *completion_options) {
+ (void)client;
AWS_PRECONDITION(allocator != NULL);
AWS_PRECONDITION(subscribe_options != NULL);
@@ -3032,12 +2956,6 @@ struct aws_mqtt5_operation_subscribe *aws_mqtt5_operation_subscribe_new(
return NULL;
}
- if (client != NULL && client->config->extended_validation_and_flow_control_options != AWS_MQTT5_EVAFCO_NONE) {
- if (aws_mqtt5_packet_subscribe_view_validate_vs_iot_core(subscribe_options)) {
- return NULL;
- }
- }
-
struct aws_mqtt5_operation_subscribe *subscribe_op =
aws_mem_calloc(allocator, 1, sizeof(struct aws_mqtt5_operation_subscribe));
if (subscribe_op == NULL) {
@@ -3048,6 +2966,7 @@ struct aws_mqtt5_operation_subscribe *aws_mqtt5_operation_subscribe_new(
subscribe_op->base.vtable = &s_subscribe_operation_vtable;
subscribe_op->base.packet_type = AWS_MQTT5_PT_SUBSCRIBE;
aws_ref_count_init(&subscribe_op->base.ref_count, subscribe_op, s_destroy_operation_subscribe);
+ aws_priority_queue_node_init(&subscribe_op->base.priority_queue_node);
subscribe_op->base.impl = subscribe_op;
if (aws_mqtt5_packet_subscribe_storage_init(&subscribe_op->options_storage, allocator, subscribe_options)) {
@@ -3383,6 +3302,7 @@ struct aws_mqtt5_operation_pingreq *aws_mqtt5_operation_pingreq_new(struct aws_a
pingreq_op->base.vtable = &s_empty_operation_vtable;
pingreq_op->base.packet_type = AWS_MQTT5_PT_PINGREQ;
aws_ref_count_init(&pingreq_op->base.ref_count, pingreq_op, s_destroy_operation_pingreq);
+ aws_priority_queue_node_init(&pingreq_op->base.priority_queue_node);
pingreq_op->base.impl = pingreq_op;
return pingreq_op;
@@ -3416,14 +3336,20 @@ int aws_mqtt5_client_options_validate(const struct aws_mqtt5_client_options *opt
}
}
+ if (aws_socket_validate_port_for_connect(
+ options->port, options->socket_options ? options->socket_options->domain : AWS_SOCKET_IPV4)) {
+ AWS_LOGF_ERROR(AWS_LS_MQTT5_GENERAL, "invalid port in mqtt5 client configuration");
+ return aws_raise_error(AWS_ERROR_MQTT5_CLIENT_OPTIONS_VALIDATION);
+ }
+
if (options->http_proxy_options != NULL) {
if (options->http_proxy_options->host.len == 0) {
AWS_LOGF_ERROR(AWS_LS_MQTT5_GENERAL, "proxy host name not set in mqtt5 client configuration");
return aws_raise_error(AWS_ERROR_MQTT5_CLIENT_OPTIONS_VALIDATION);
}
- if (options->http_proxy_options->port == 0) {
- AWS_LOGF_ERROR(AWS_LS_MQTT5_GENERAL, "proxy port not set in mqtt5 client configuration");
+ if (aws_socket_validate_port_for_connect(options->http_proxy_options->port, AWS_SOCKET_IPV4)) {
+ AWS_LOGF_ERROR(AWS_LS_MQTT5_GENERAL, "invalid proxy port in mqtt5 client configuration");
return aws_raise_error(AWS_ERROR_MQTT5_CLIENT_OPTIONS_VALIDATION);
}
}
@@ -3441,32 +3367,20 @@ int aws_mqtt5_client_options_validate(const struct aws_mqtt5_client_options *opt
if (aws_mqtt5_packet_connect_view_validate(options->connect_options)) {
AWS_LOGF_ERROR(AWS_LS_MQTT5_GENERAL, "invalid CONNECT options in mqtt5 client configuration");
+ /* connect validation failure will have already raised the appropriate error */
return AWS_OP_ERR;
}
- /* The client will not behave properly if ping timeout is not significantly shorter than the keep alive interval */
- if (options->connect_options->keep_alive_interval_seconds > 0) {
- uint64_t keep_alive_ms = aws_timestamp_convert(
- options->connect_options->keep_alive_interval_seconds, AWS_TIMESTAMP_SECS, AWS_TIMESTAMP_MILLIS, NULL);
- uint64_t one_second_ms = aws_timestamp_convert(1, AWS_TIMESTAMP_SECS, AWS_TIMESTAMP_MILLIS, NULL);
-
- uint64_t ping_timeout_ms = options->ping_timeout_ms;
- if (ping_timeout_ms == 0) {
- ping_timeout_ms = AWS_MQTT5_CLIENT_DEFAULT_PING_TIMEOUT_MS;
- }
-
- if (ping_timeout_ms + one_second_ms > keep_alive_ms) {
- AWS_LOGF_ERROR(AWS_LS_MQTT5_GENERAL, "keep alive interval is too small relative to ping timeout interval");
- return AWS_OP_ERR;
+ if (options->topic_aliasing_options != NULL) {
+ if (!aws_mqtt5_outbound_topic_alias_behavior_type_validate(
+ options->topic_aliasing_options->outbound_topic_alias_behavior)) {
+ AWS_LOGF_ERROR(AWS_LS_MQTT5_GENERAL, "invalid outbound topic alias behavior type value");
+ return aws_raise_error(AWS_ERROR_MQTT5_CLIENT_OPTIONS_VALIDATION);
}
- }
- if (options->extended_validation_and_flow_control_options != AWS_MQTT5_EVAFCO_NONE) {
- if (options->connect_options->client_id.len > AWS_IOT_CORE_MAXIMUM_CLIENT_ID_LENGTH) {
- AWS_LOGF_ERROR(
- AWS_LS_MQTT5_GENERAL,
- "AWS IoT Core limits client_id to be less than or equal to %d bytes in length",
- (int)AWS_IOT_CORE_MAXIMUM_CLIENT_ID_LENGTH);
+ if (!aws_mqtt5_inbound_topic_alias_behavior_type_validate(
+ options->topic_aliasing_options->inbound_topic_alias_behavior)) {
+ AWS_LOGF_ERROR(AWS_LS_MQTT5_GENERAL, "invalid inbound topic alias behavior type value");
return aws_raise_error(AWS_ERROR_MQTT5_CLIENT_OPTIONS_VALIDATION);
}
}
@@ -3607,7 +3521,7 @@ void aws_mqtt5_client_options_storage_log(
log_handle,
level,
AWS_LS_MQTT5_GENERAL,
- "id=%p: aws_mqtt5_client_options_storage port set to %" PRIu16,
+ "id=%p: aws_mqtt5_client_options_storage port set to %" PRIu32,
(void *)options_storage,
options_storage->port);
@@ -3668,7 +3582,7 @@ void aws_mqtt5_client_options_storage_log(
log_handle,
level,
AWS_LS_MQTT5_GENERAL,
- "id=%p: aws_mqtt5_client_options_storage http proxy port set to %" PRIu16,
+ "id=%p: aws_mqtt5_client_options_storage http proxy port set to %" PRIu32,
(void *)options_storage,
options_storage->http_proxy_options.port);
@@ -3794,7 +3708,7 @@ void aws_mqtt5_client_options_storage_log(
"id=%p: aws_mqtt5_client_options_storage connect options:",
(void *)options_storage);
- aws_mqtt5_packet_connect_view_log(&options_storage->connect.storage_view, level);
+ aws_mqtt5_packet_connect_view_log(&options_storage->connect->storage_view, level);
AWS_LOGUF(
log_handle,
@@ -3816,7 +3730,8 @@ void aws_mqtt5_client_options_storage_destroy(struct aws_mqtt5_client_options_st
aws_tls_connection_options_clean_up(&options_storage->tls_options);
aws_http_proxy_config_destroy(options_storage->http_proxy_config);
- aws_mqtt5_packet_connect_storage_clean_up(&options_storage->connect);
+ aws_mqtt5_packet_connect_storage_clean_up(options_storage->connect);
+ aws_mem_release(options_storage->connect->allocator, options_storage->connect);
aws_mem_release(options_storage->allocator, options_storage);
}
@@ -3944,7 +3859,8 @@ struct aws_mqtt5_client_options_storage *aws_mqtt5_client_options_storage_new(
options_storage->topic_aliasing_options = *options->topic_aliasing_options;
}
- if (aws_mqtt5_packet_connect_storage_init(&options_storage->connect, allocator, options->connect_options)) {
+ options_storage->connect = aws_mem_calloc(allocator, 1, sizeof(struct aws_mqtt5_packet_connect_storage));
+ if (aws_mqtt5_packet_connect_storage_init(options_storage->connect, allocator, options->connect_options)) {
goto error;
}
@@ -3956,6 +3872,15 @@ struct aws_mqtt5_client_options_storage *aws_mqtt5_client_options_storage_new(
s_apply_zero_valued_defaults_to_client_options_storage(options_storage);
+ /* must do this after zero-valued defaults are applied so that max reconnect is accurate */
+ if (options->host_resolution_override) {
+ options_storage->host_resolution_override = *options->host_resolution_override;
+ } else {
+ options_storage->host_resolution_override = aws_host_resolver_init_default_resolution_config();
+ options_storage->host_resolution_override.resolve_frequency_ns = aws_timestamp_convert(
+ options_storage->max_reconnect_delay_ms, AWS_TIMESTAMP_MILLIS, AWS_TIMESTAMP_NANOS, NULL);
+ }
+
return options_storage;
error:
diff --git a/contrib/restricted/aws/aws-c-mqtt/source/v5/mqtt5_to_mqtt3_adapter.c b/contrib/restricted/aws/aws-c-mqtt/source/v5/mqtt5_to_mqtt3_adapter.c
new file mode 100644
index 00000000000..bd90a6581fa
--- /dev/null
+++ b/contrib/restricted/aws/aws-c-mqtt/source/v5/mqtt5_to_mqtt3_adapter.c
@@ -0,0 +1,3039 @@
+/**
+ * Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved.
+ * SPDX-License-Identifier: Apache-2.0.
+ */
+
+#include <aws/mqtt/mqtt.h>
+
+#include <aws/common/clock.h>
+#include <aws/common/rw_lock.h>
+
+#include <aws/mqtt/private/client_impl_shared.h>
+#include <aws/mqtt/private/mqtt_subscription_set.h>
+#include <aws/mqtt/private/v5/mqtt5_client_impl.h>
+#include <aws/mqtt/private/v5/mqtt5_to_mqtt3_adapter_impl.h>
+#include <aws/mqtt/v5/mqtt5_listener.h>
+
+/*
+ * A best-effort-but-not-100%-accurate translation from mqtt5 error codes to mqtt311 error codes.
+ */
+static int s_translate_mqtt5_error_code_to_mqtt311(int error_code) {
+ switch (error_code) {
+ case AWS_ERROR_MQTT5_ENCODE_FAILURE:
+ case AWS_ERROR_MQTT5_DECODE_PROTOCOL_ERROR:
+ return AWS_ERROR_MQTT_PROTOCOL_ERROR;
+
+ case AWS_ERROR_MQTT5_CONNACK_CONNECTION_REFUSED:
+ return AWS_ERROR_MQTT_PROTOCOL_ERROR; /* a decidedly strange choice by the 311 implementation */
+
+ case AWS_ERROR_MQTT5_CONNACK_TIMEOUT:
+ case AWS_ERROR_MQTT5_PING_RESPONSE_TIMEOUT:
+ return AWS_ERROR_MQTT_TIMEOUT;
+
+ case AWS_ERROR_MQTT5_USER_REQUESTED_STOP:
+ case AWS_ERROR_MQTT5_CLIENT_TERMINATED:
+ return AWS_IO_SOCKET_CLOSED;
+
+ case AWS_ERROR_MQTT5_DISCONNECT_RECEIVED:
+ return AWS_ERROR_MQTT_UNEXPECTED_HANGUP;
+
+ case AWS_ERROR_MQTT5_OPERATION_FAILED_DUE_TO_OFFLINE_QUEUE_POLICY:
+ return AWS_ERROR_MQTT_CANCELLED_FOR_CLEAN_SESSION;
+
+ case AWS_ERROR_MQTT5_ENCODE_SIZE_UNSUPPORTED_PACKET_TYPE:
+ return AWS_ERROR_MQTT_INVALID_PACKET_TYPE;
+
+ case AWS_ERROR_MQTT5_OPERATION_PROCESSING_FAILURE:
+ return AWS_ERROR_MQTT_PROTOCOL_ERROR;
+
+ case AWS_ERROR_MQTT5_INVALID_UTF8_STRING:
+ return AWS_ERROR_MQTT_INVALID_TOPIC;
+
+ default:
+ return error_code;
+ }
+}
+
+struct aws_mqtt_adapter_final_destroy_task {
+ struct aws_task task;
+ struct aws_allocator *allocator;
+ struct aws_mqtt_client_connection *connection;
+};
+
+static void s_mqtt_adapter_final_destroy_task_fn(struct aws_task *task, void *arg, enum aws_task_status status) {
+ (void)task;
+ (void)status;
+
+ struct aws_mqtt_adapter_final_destroy_task *destroy_task = arg;
+ struct aws_mqtt_client_connection_5_impl *adapter = destroy_task->connection->impl;
+
+ AWS_LOGF_DEBUG(AWS_LS_MQTT5_TO_MQTT3_ADAPTER, "id=%p: Final destruction of mqtt3-to-5 adapter", (void *)adapter);
+
+ aws_mqtt_client_on_connection_termination_fn *termination_handler = NULL;
+ void *termination_handler_user_data = NULL;
+ if (adapter->on_termination != NULL) {
+ termination_handler = adapter->on_termination;
+ termination_handler_user_data = adapter->on_termination_user_data;
+ }
+
+ if (adapter->client->config->websocket_handshake_transform_user_data == adapter) {
+ /*
+ * If the mqtt5 client is pointing to us for websocket transform, then erase that. The callback
+ * is invoked from our pinned event loop so this is safe.
+ *
+ * TODO: It is possible that multiple adapters may have sequentially side-affected the websocket handshake.
+ * For now, in that case, subsequent connection attempts will probably not succeed.
+ */
+ adapter->client->config->websocket_handshake_transform = NULL;
+ adapter->client->config->websocket_handshake_transform_user_data = NULL;
+ }
+
+ aws_mqtt_subscription_set_destroy(adapter->subscriptions);
+ aws_mqtt5_to_mqtt3_adapter_operation_table_clean_up(&adapter->operational_state);
+
+ adapter->client = aws_mqtt5_client_release(adapter->client);
+
+ aws_mem_release(adapter->allocator, adapter);
+
+ aws_mem_release(destroy_task->allocator, destroy_task);
+
+ /* trigger the termination callback */
+ if (termination_handler) {
+ termination_handler(termination_handler_user_data);
+ }
+}
+
+static struct aws_mqtt_adapter_final_destroy_task *s_aws_mqtt_adapter_final_destroy_task_new(
+ struct aws_allocator *allocator,
+ struct aws_mqtt_client_connection_5_impl *adapter) {
+
+ struct aws_mqtt_adapter_final_destroy_task *destroy_task =
+ aws_mem_calloc(allocator, 1, sizeof(struct aws_mqtt_adapter_final_destroy_task));
+
+ aws_task_init(
+ &destroy_task->task, s_mqtt_adapter_final_destroy_task_fn, (void *)destroy_task, "MqttAdapterFinalDestroy");
+ destroy_task->allocator = adapter->allocator;
+ destroy_task->connection = &adapter->base; /* Do not acquire, we're at zero external and internal ref counts */
+
+ return destroy_task;
+}
+
+static void s_aws_mqtt_adapter_final_destroy(struct aws_mqtt_client_connection_5_impl *adapter) {
+
+ struct aws_mqtt_adapter_final_destroy_task *task =
+ s_aws_mqtt_adapter_final_destroy_task_new(adapter->allocator, adapter);
+ if (task == NULL) {
+ int error_code = aws_last_error();
+ AWS_LOGF_ERROR(
+ AWS_LS_MQTT5_TO_MQTT3_ADAPTER,
+ "id=%p: failed to create adapter final destroy task, last_error: %d(%s)",
+ (void *)adapter,
+ error_code,
+ aws_error_debug_str(error_code));
+ return;
+ }
+
+ aws_event_loop_schedule_task_now(adapter->loop, &task->task);
+}
+
+struct aws_mqtt_adapter_disconnect_task {
+ struct aws_task task;
+ struct aws_allocator *allocator;
+ struct aws_mqtt_client_connection_5_impl *adapter;
+
+ aws_mqtt_client_on_disconnect_fn *on_disconnect;
+ void *on_disconnect_user_data;
+};
+
+static void s_adapter_disconnect_task_fn(struct aws_task *task, void *arg, enum aws_task_status status);
+
+static struct aws_mqtt_adapter_disconnect_task *s_aws_mqtt_adapter_disconnect_task_new(
+ struct aws_allocator *allocator,
+ struct aws_mqtt_client_connection_5_impl *adapter,
+ aws_mqtt_client_on_disconnect_fn *on_disconnect,
+ void *on_disconnect_user_data) {
+
+ struct aws_mqtt_adapter_disconnect_task *disconnect_task =
+ aws_mem_calloc(allocator, 1, sizeof(struct aws_mqtt_adapter_disconnect_task));
+
+ aws_task_init(
+ &disconnect_task->task, s_adapter_disconnect_task_fn, (void *)disconnect_task, "AdapterDisconnectTask");
+ disconnect_task->allocator = adapter->allocator;
+ disconnect_task->adapter =
+ (struct aws_mqtt_client_connection_5_impl *)aws_ref_count_acquire(&adapter->internal_refs);
+
+ disconnect_task->on_disconnect = on_disconnect;
+ disconnect_task->on_disconnect_user_data = on_disconnect_user_data;
+
+ return disconnect_task;
+}
+
+static int s_aws_mqtt_client_connection_5_disconnect(
+ void *impl,
+ aws_mqtt_client_on_disconnect_fn *on_disconnect,
+ void *on_disconnect_user_data) {
+
+ struct aws_mqtt_client_connection_5_impl *adapter = impl;
+
+ struct aws_mqtt_adapter_disconnect_task *task =
+ s_aws_mqtt_adapter_disconnect_task_new(adapter->allocator, adapter, on_disconnect, on_disconnect_user_data);
+ if (task == NULL) {
+ int error_code = aws_last_error();
+ AWS_LOGF_ERROR(
+ AWS_LS_MQTT5_TO_MQTT3_ADAPTER,
+ "id=%p: failed to create adapter disconnect task, error code %d(%s)",
+ (void *)adapter,
+ error_code,
+ aws_error_debug_str(error_code));
+ return AWS_OP_ERR;
+ }
+
+ aws_event_loop_schedule_task_now(adapter->loop, &task->task);
+
+ return AWS_OP_SUCCESS;
+}
+
+struct aws_mqtt_adapter_connect_task {
+ struct aws_task task;
+ struct aws_allocator *allocator;
+ struct aws_mqtt_client_connection_5_impl *adapter;
+
+ struct aws_byte_buf host_name;
+ uint32_t port;
+ struct aws_socket_options socket_options;
+ struct aws_tls_connection_options *tls_options_ptr;
+ struct aws_tls_connection_options tls_options;
+
+ struct aws_byte_buf client_id;
+ uint16_t keep_alive_time_secs;
+ uint32_t ping_timeout_ms;
+ uint32_t protocol_operation_timeout_ms;
+ aws_mqtt_client_on_connection_complete_fn *on_connection_complete;
+ void *on_connection_complete_user_data;
+ bool clean_session;
+};
+
+static void s_aws_mqtt_adapter_connect_task_destroy(struct aws_mqtt_adapter_connect_task *task) {
+ if (task == NULL) {
+ return;
+ }
+
+ aws_byte_buf_clean_up(&task->host_name);
+ aws_byte_buf_clean_up(&task->client_id);
+
+ if (task->tls_options_ptr) {
+ aws_tls_connection_options_clean_up(task->tls_options_ptr);
+ }
+
+ aws_mem_release(task->allocator, task);
+}
+
+static void s_adapter_connect_task_fn(struct aws_task *task, void *arg, enum aws_task_status status);
+
+static struct aws_mqtt_adapter_connect_task *s_aws_mqtt_adapter_connect_task_new(
+ struct aws_allocator *allocator,
+ struct aws_mqtt_client_connection_5_impl *adapter,
+ const struct aws_mqtt_connection_options *connection_options) {
+
+ struct aws_mqtt_adapter_connect_task *connect_task =
+ aws_mem_calloc(allocator, 1, sizeof(struct aws_mqtt_adapter_connect_task));
+
+ aws_task_init(&connect_task->task, s_adapter_connect_task_fn, (void *)connect_task, "AdapterConnectTask");
+ connect_task->allocator = adapter->allocator;
+
+ aws_byte_buf_init_copy_from_cursor(&connect_task->host_name, allocator, connection_options->host_name);
+ connect_task->port = connection_options->port;
+ connect_task->socket_options = *connection_options->socket_options;
+ if (connection_options->tls_options) {
+ if (aws_tls_connection_options_copy(&connect_task->tls_options, connection_options->tls_options)) {
+ goto error;
+ }
+ connect_task->tls_options_ptr = &connect_task->tls_options;
+
+ /* Cheat and set the tls_options host_name to our copy if they're the same */
+ if (!connect_task->tls_options.server_name) {
+ struct aws_byte_cursor host_name_cur = aws_byte_cursor_from_buf(&connect_task->host_name);
+
+ if (aws_tls_connection_options_set_server_name(
+ &connect_task->tls_options, connect_task->allocator, &host_name_cur)) {
+ AWS_LOGF_ERROR(
+ AWS_LS_MQTT5_TO_MQTT3_ADAPTER,
+ "id=%p: mqtt3-to-5-adapter - Failed to set TLS Connection Options server name",
+ (void *)adapter);
+ goto error;
+ }
+ }
+ }
+ connect_task->adapter = (struct aws_mqtt_client_connection_5_impl *)aws_ref_count_acquire(&adapter->internal_refs);
+ aws_byte_buf_init_copy_from_cursor(&connect_task->client_id, allocator, connection_options->client_id);
+
+ connect_task->keep_alive_time_secs = connection_options->keep_alive_time_secs;
+ connect_task->ping_timeout_ms = connection_options->ping_timeout_ms;
+ connect_task->protocol_operation_timeout_ms = connection_options->protocol_operation_timeout_ms;
+ connect_task->on_connection_complete = connection_options->on_connection_complete;
+ connect_task->on_connection_complete_user_data = connection_options->user_data;
+ connect_task->clean_session = connection_options->clean_session;
+
+ return connect_task;
+
+error:
+ s_aws_mqtt_adapter_connect_task_destroy(connect_task);
+
+ return NULL;
+}
+
+static int s_validate_adapter_connection_options(
+ const struct aws_mqtt_connection_options *connection_options,
+ struct aws_mqtt_client_connection_5_impl *adapter) {
+ if (connection_options == NULL) {
+ return aws_raise_error(AWS_ERROR_MQTT5_CLIENT_OPTIONS_VALIDATION);
+ }
+
+ if (connection_options->host_name.len == 0) {
+ AWS_LOGF_ERROR(
+ AWS_LS_MQTT5_TO_MQTT3_ADAPTER,
+ "id=%p: mqtt3-to-5-adapter - host name not set in MQTT client configuration",
+ (void *)adapter);
+ return aws_raise_error(AWS_ERROR_MQTT5_CLIENT_OPTIONS_VALIDATION);
+ }
+
+ /* forbid no-timeout until someone convinces me otherwise */
+ if (connection_options->socket_options != NULL) {
+ if (connection_options->socket_options->type == AWS_SOCKET_DGRAM ||
+ connection_options->socket_options->connect_timeout_ms == 0) {
+ AWS_LOGF_ERROR(
+ AWS_LS_MQTT5_TO_MQTT3_ADAPTER,
+ "id=%p: mqtt3-to-5-adapter - invalid socket options in MQTT client configuration",
+ (void *)adapter);
+ return aws_raise_error(AWS_ERROR_MQTT5_CLIENT_OPTIONS_VALIDATION);
+ }
+ }
+
+ return AWS_OP_SUCCESS;
+}
+
+static int s_aws_mqtt_client_connection_5_connect(
+ void *impl,
+ const struct aws_mqtt_connection_options *connection_options) {
+
+ struct aws_mqtt_client_connection_5_impl *adapter = impl;
+
+ /* The client will not behave properly if ping timeout is not significantly shorter than the keep alive interval */
+ if (s_validate_adapter_connection_options(connection_options, adapter)) {
+ return AWS_OP_ERR;
+ }
+
+ struct aws_mqtt_adapter_connect_task *task =
+ s_aws_mqtt_adapter_connect_task_new(adapter->allocator, adapter, connection_options);
+ if (task == NULL) {
+ int error_code = aws_last_error();
+ AWS_LOGF_ERROR(
+ AWS_LS_MQTT5_TO_MQTT3_ADAPTER,
+ "id=%p: mqtt3-to-5-adapter - failed to create adapter connect task, error code %d(%s)",
+ (void *)adapter,
+ error_code,
+ aws_error_debug_str(error_code));
+ return AWS_OP_ERR;
+ }
+
+ aws_event_loop_schedule_task_now(adapter->loop, &task->task);
+
+ return AWS_OP_SUCCESS;
+}
+
+static void s_aws_mqtt5_to_mqtt3_adapter_lifecycle_handler(const struct aws_mqtt5_client_lifecycle_event *event) {
+ struct aws_mqtt_client_connection_5_impl *adapter = event->user_data;
+
+ switch (event->event_type) {
+
+ case AWS_MQTT5_CLET_CONNECTION_SUCCESS:
+ AWS_LOGF_DEBUG(
+ AWS_LS_MQTT5_TO_MQTT3_ADAPTER,
+ "id=%p: mqtt3-to-5-adapter - received on connection success event from mqtt5 client, adapter in state "
+ "(%d)",
+ (void *)adapter,
+ (int)adapter->adapter_state);
+ if (adapter->adapter_state != AWS_MQTT_AS_STAY_DISCONNECTED) {
+ if (adapter->on_connection_success != NULL) {
+ (*adapter->on_connection_success)(
+ &adapter->base, 0, event->settings->rejoined_session, adapter->on_connection_success_user_data);
+ }
+
+ if (adapter->adapter_state == AWS_MQTT_AS_FIRST_CONNECT) {
+ /*
+ * If the 311 view is that this is an initial connection attempt, then invoke the completion
+ * callback and move to the stay-connected state.
+ */
+ if (adapter->on_connection_complete != NULL) {
+ (*adapter->on_connection_complete)(
+ &adapter->base,
+ event->error_code,
+ 0,
+ event->settings->rejoined_session,
+ adapter->on_connection_complete_user_data);
+
+ adapter->on_connection_complete = NULL;
+ adapter->on_connection_complete_user_data = NULL;
+ }
+ adapter->adapter_state = AWS_MQTT_AS_STAY_CONNECTED;
+ } else if (adapter->adapter_state == AWS_MQTT_AS_STAY_CONNECTED) {
+ /*
+ * If the 311 view is that we're in the stay-connected state (ie we've successfully done or
+ * simulated an initial connection), then invoke the connection resumption callback.
+ */
+ if (adapter->on_resumed != NULL) {
+ (*adapter->on_resumed)(
+ &adapter->base, 0, event->settings->rejoined_session, adapter->on_resumed_user_data);
+ }
+ }
+ }
+ break;
+
+ case AWS_MQTT5_CLET_CONNECTION_FAILURE:
+ AWS_LOGF_DEBUG(
+ AWS_LS_MQTT5_TO_MQTT3_ADAPTER,
+ "id=%p: mqtt3-to-5-adapter - received on connection failure event from mqtt5 client, adapter in state "
+ "(%d)",
+ (void *)adapter,
+ (int)adapter->adapter_state);
+
+ /*
+ * The MQTT311 interface only cares about connection failures when it's the initial connection attempt
+ * after a call to connect(). Since an adapter connect() can sever an existing connection (with an
+ * error code of AWS_ERROR_MQTT_CONNECTION_RESET_FOR_ADAPTER_CONNECT) we only react to connection failures
+ * if
+ * (1) the error code is not AWS_ERROR_MQTT_CONNECTION_RESET_FOR_ADAPTER_CONNECT and
+ * (2) we're in the FIRST_CONNECT state
+ *
+ * Only if both of these are true should we invoke the connection completion callback with a failure and
+ * put the adapter into the "disconnected" state, simulating the way the 311 client stops after an
+ * initial connection failure.
+ */
+ if (event->error_code != AWS_ERROR_MQTT_CONNECTION_RESET_FOR_ADAPTER_CONNECT) {
+ if (adapter->adapter_state != AWS_MQTT_AS_STAY_DISCONNECTED) {
+ int mqtt311_error_code = s_translate_mqtt5_error_code_to_mqtt311(event->error_code);
+
+ if (adapter->on_connection_failure != NULL) {
+ (*adapter->on_connection_failure)(
+ &adapter->base, mqtt311_error_code, adapter->on_connection_failure_user_data);
+ }
+
+ if (adapter->adapter_state == AWS_MQTT_AS_FIRST_CONNECT) {
+ if (adapter->on_connection_complete != NULL) {
+ (*adapter->on_connection_complete)(
+ &adapter->base,
+ mqtt311_error_code,
+ 0,
+ false,
+ adapter->on_connection_complete_user_data);
+
+ adapter->on_connection_complete = NULL;
+ adapter->on_connection_complete_user_data = NULL;
+ }
+
+ adapter->adapter_state = AWS_MQTT_AS_STAY_DISCONNECTED;
+ }
+ }
+ }
+
+ break;
+
+ case AWS_MQTT5_CLET_DISCONNECTION:
+ AWS_LOGF_DEBUG(
+ AWS_LS_MQTT5_TO_MQTT3_ADAPTER,
+ "id=%p: mqtt3-to-5-adapter - received on disconnection event from mqtt5 client, adapter in state (%d), "
+ "error code (%d)",
+ (void *)adapter,
+ (int)adapter->adapter_state,
+ event->error_code);
+ /*
+ * If the 311 view is that we're in the stay-connected state (ie we've successfully done or simulated
+ * an initial connection), then invoke the connection interrupted callback.
+ */
+ if (adapter->on_interrupted != NULL && adapter->adapter_state == AWS_MQTT_AS_STAY_CONNECTED &&
+ event->error_code != AWS_ERROR_MQTT_CONNECTION_RESET_FOR_ADAPTER_CONNECT) {
+
+ (*adapter->on_interrupted)(
+ &adapter->base,
+ s_translate_mqtt5_error_code_to_mqtt311(event->error_code),
+ adapter->on_interrupted_user_data);
+ }
+ break;
+
+ case AWS_MQTT5_CLET_STOPPED:
+ AWS_LOGF_DEBUG(
+ AWS_LS_MQTT5_TO_MQTT3_ADAPTER,
+ "id=%p: mqtt3-to-5-adapter - received on stopped event from mqtt5 client, adapter in state (%d)",
+ (void *)adapter,
+ (int)adapter->adapter_state);
+
+ /* If an MQTT311-view user is waiting on a disconnect callback, invoke it */
+ if (adapter->on_disconnect) {
+ (*adapter->on_disconnect)(&adapter->base, adapter->on_disconnect_user_data);
+
+ adapter->on_disconnect = NULL;
+ adapter->on_disconnect_user_data = NULL;
+ }
+
+ if (adapter->on_closed) {
+ (*adapter->on_closed)(&adapter->base, NULL, adapter->on_closed_user_data);
+ }
+
+ /*
+ * Judgement call: If the mqtt5 client is stopped behind our back, it seems better to transition to the
+ * disconnected state (which only requires a connect() to restart) then stay in the STAY_CONNECTED state
+ * which currently requires a disconnect() and then a connect() to restore connectivity.
+ *
+ * ToDo: what if we disabled mqtt5 client start/stop somehow while the adapter is attached, preventing
+ * the potential to backstab each other? Unfortunately neither start() nor stop() have an error reporting
+ * mechanism.
+ */
+ adapter->adapter_state = AWS_MQTT_AS_STAY_DISCONNECTED;
+ break;
+
+ default:
+ break;
+ }
+}
+
+static void s_aws_mqtt5_to_mqtt3_adapter_disconnect_handler(
+ struct aws_mqtt_client_connection_5_impl *adapter,
+ struct aws_mqtt_adapter_disconnect_task *disconnect_task) {
+
+ AWS_LOGF_DEBUG(
+ AWS_LS_MQTT5_TO_MQTT3_ADAPTER,
+ "id=%p: mqtt3-to-5-adapter - performing disconnect safe callback, adapter in state (%d)",
+ (void *)adapter,
+ (int)adapter->adapter_state);
+
+ /*
+ * If we're already disconnected (from the 311 perspective only), then invoke the callback and return
+ */
+ if (adapter->adapter_state == AWS_MQTT_AS_STAY_DISCONNECTED) {
+ if (disconnect_task->on_disconnect) {
+ (*disconnect_task->on_disconnect)(&adapter->base, disconnect_task->on_disconnect_user_data);
+ }
+
+ return;
+ }
+
+ /*
+ * If we had a pending first connect, then notify failure
+ */
+ if (adapter->adapter_state == AWS_MQTT_AS_FIRST_CONNECT) {
+ if (adapter->on_connection_complete != NULL) {
+ (*adapter->on_connection_complete)(
+ &adapter->base,
+ AWS_ERROR_MQTT_CONNECTION_SHUTDOWN,
+ 0,
+ false,
+ adapter->on_connection_complete_user_data);
+
+ adapter->on_connection_complete = NULL;
+ adapter->on_connection_complete_user_data = NULL;
+ }
+ }
+
+ adapter->adapter_state = AWS_MQTT_AS_STAY_DISCONNECTED;
+
+ bool invoke_callbacks = true;
+ if (adapter->client->desired_state != AWS_MCS_STOPPED) {
+ AWS_LOGF_DEBUG(
+ AWS_LS_MQTT5_TO_MQTT3_ADAPTER,
+ "id=%p: mqtt3-to-5-adapter - disconnect forwarding stop request to mqtt5 client",
+ (void *)adapter);
+
+ aws_mqtt5_client_change_desired_state(adapter->client, AWS_MCS_STOPPED, NULL);
+
+ adapter->on_disconnect = disconnect_task->on_disconnect;
+ adapter->on_disconnect_user_data = disconnect_task->on_disconnect_user_data;
+ invoke_callbacks = false;
+ }
+
+ if (invoke_callbacks) {
+ if (disconnect_task->on_disconnect != NULL) {
+ (*disconnect_task->on_disconnect)(&adapter->base, disconnect_task->on_disconnect_user_data);
+ }
+
+ if (adapter->on_closed) {
+ (*adapter->on_closed)(&adapter->base, NULL, adapter->on_closed_user_data);
+ }
+ }
+}
+
+static void s_adapter_disconnect_task_fn(struct aws_task *task, void *arg, enum aws_task_status status) {
+ (void)task;
+
+ struct aws_mqtt_adapter_disconnect_task *disconnect_task = arg;
+ struct aws_mqtt_client_connection_5_impl *adapter = disconnect_task->adapter;
+ if (status != AWS_TASK_STATUS_RUN_READY) {
+ goto done;
+ }
+
+ s_aws_mqtt5_to_mqtt3_adapter_disconnect_handler(adapter, disconnect_task);
+
+done:
+
+ aws_ref_count_release(&adapter->internal_refs);
+
+ aws_mem_release(disconnect_task->allocator, disconnect_task);
+}
+
+static void s_aws_mqtt5_to_mqtt3_adapter_update_config_on_connect(
+ struct aws_mqtt_client_connection_5_impl *adapter,
+ struct aws_mqtt_adapter_connect_task *connect_task) {
+ struct aws_mqtt5_client_options_storage *config = adapter->client->config;
+
+ aws_string_destroy(config->host_name);
+ config->host_name = aws_string_new_from_buf(adapter->allocator, &connect_task->host_name);
+ config->port = connect_task->port;
+ config->socket_options = connect_task->socket_options;
+
+ if (config->tls_options_ptr) {
+ aws_tls_connection_options_clean_up(&config->tls_options);
+ config->tls_options_ptr = NULL;
+ }
+
+ if (connect_task->tls_options_ptr) {
+ aws_tls_connection_options_copy(&config->tls_options, connect_task->tls_options_ptr);
+ config->tls_options_ptr = &config->tls_options;
+ }
+
+ aws_byte_buf_clean_up(&adapter->client->negotiated_settings.client_id_storage);
+ aws_byte_buf_init_copy_from_cursor(
+ &adapter->client->negotiated_settings.client_id_storage,
+ adapter->allocator,
+ aws_byte_cursor_from_buf(&connect_task->client_id));
+
+ config->connect->storage_view.keep_alive_interval_seconds = connect_task->keep_alive_time_secs;
+ config->ping_timeout_ms = connect_task->ping_timeout_ms;
+
+ /* Override timeout, rounding up as necessary */
+ config->ack_timeout_seconds = (uint32_t)aws_timestamp_convert(
+ connect_task->protocol_operation_timeout_ms + AWS_TIMESTAMP_MILLIS - 1,
+ AWS_TIMESTAMP_MILLIS,
+ AWS_TIMESTAMP_SECS,
+ NULL);
+
+ if (connect_task->clean_session) {
+ config->session_behavior = AWS_MQTT5_CSBT_CLEAN;
+ config->connect->storage_view.session_expiry_interval_seconds = NULL;
+ } else {
+ config->session_behavior = AWS_MQTT5_CSBT_REJOIN_ALWAYS;
+ /* This is a judgement call to translate session expiry to the maximum possible allowed by AWS IoT Core */
+ config->connect->session_expiry_interval_seconds = 7 * 24 * 60 * 60;
+ config->connect->storage_view.session_expiry_interval_seconds =
+ &config->connect->session_expiry_interval_seconds;
+ }
+}
+
+static void s_aws_mqtt5_to_mqtt3_adapter_connect_handler(
+ struct aws_mqtt_client_connection_5_impl *adapter,
+ struct aws_mqtt_adapter_connect_task *connect_task) {
+
+ AWS_LOGF_DEBUG(
+ AWS_LS_MQTT5_TO_MQTT3_ADAPTER,
+ "id=%p: mqtt3-to-5-adapter - performing connect safe callback, adapter in state (%d)",
+ (void *)adapter,
+ (int)adapter->adapter_state);
+
+ if (adapter->adapter_state != AWS_MQTT_AS_STAY_DISCONNECTED) {
+ if (connect_task->on_connection_complete) {
+ (*connect_task->on_connection_complete)(
+ &adapter->base,
+ AWS_ERROR_MQTT_ALREADY_CONNECTED,
+ 0,
+ false,
+ connect_task->on_connection_complete_user_data);
+ }
+
+ return;
+ }
+
+ if (adapter->on_disconnect) {
+ (*adapter->on_disconnect)(&adapter->base, adapter->on_disconnect_user_data);
+
+ adapter->on_disconnect = NULL;
+ adapter->on_disconnect_user_data = NULL;
+ }
+
+ adapter->adapter_state = AWS_MQTT_AS_FIRST_CONNECT;
+
+ AWS_LOGF_DEBUG(
+ AWS_LS_MQTT5_TO_MQTT3_ADAPTER,
+ "id=%p: mqtt3-to-5-adapter - resetting mqtt5 client connection and requesting start",
+ (void *)adapter);
+
+ /* Update mqtt5 config */
+ s_aws_mqtt5_to_mqtt3_adapter_update_config_on_connect(adapter, connect_task);
+
+ aws_mqtt5_client_reset_connection(adapter->client);
+
+ aws_mqtt5_client_change_desired_state(adapter->client, AWS_MCS_CONNECTED, NULL);
+
+ adapter->on_connection_complete = connect_task->on_connection_complete;
+ adapter->on_connection_complete_user_data = connect_task->on_connection_complete_user_data;
+}
+
+static void s_adapter_connect_task_fn(struct aws_task *task, void *arg, enum aws_task_status status) {
+ (void)task;
+
+ struct aws_mqtt_adapter_connect_task *connect_task = arg;
+ struct aws_mqtt_client_connection_5_impl *adapter = connect_task->adapter;
+ if (status != AWS_TASK_STATUS_RUN_READY) {
+ goto done;
+ }
+
+ s_aws_mqtt5_to_mqtt3_adapter_connect_handler(adapter, connect_task);
+
+done:
+
+ aws_ref_count_release(&adapter->internal_refs);
+
+ s_aws_mqtt_adapter_connect_task_destroy(connect_task);
+}
+
+static bool s_aws_mqtt5_listener_publish_received_adapter(
+ const struct aws_mqtt5_packet_publish_view *publish,
+ void *user_data) {
+
+ struct aws_mqtt_client_connection_5_impl *adapter = user_data;
+ struct aws_mqtt_client_connection *connection = &adapter->base;
+
+ struct aws_mqtt_subscription_set_publish_received_options incoming_publish_options = {
+ .connection = connection,
+ .topic = publish->topic,
+ .qos = (enum aws_mqtt_qos)publish->qos,
+ .retain = publish->retain,
+ .dup = publish->duplicate,
+ .payload = publish->payload,
+ };
+
+ aws_mqtt_subscription_set_on_publish_received(adapter->subscriptions, &incoming_publish_options);
+
+ if (adapter->on_any_publish) {
+ (*adapter->on_any_publish)(
+ connection,
+ &publish->topic,
+ &publish->payload,
+ publish->duplicate,
+ (enum aws_mqtt_qos)publish->qos,
+ publish->retain,
+ adapter->on_any_publish_user_data);
+ }
+
+ return false;
+}
+
+struct aws_mqtt_set_interruption_handlers_task {
+ struct aws_task task;
+ struct aws_allocator *allocator;
+ struct aws_mqtt_client_connection_5_impl *adapter;
+
+ aws_mqtt_client_on_connection_interrupted_fn *on_interrupted;
+ void *on_interrupted_user_data;
+
+ aws_mqtt_client_on_connection_resumed_fn *on_resumed;
+ void *on_resumed_user_data;
+};
+
+static void s_set_interruption_handlers_task_fn(struct aws_task *task, void *arg, enum aws_task_status status) {
+ (void)task;
+
+ struct aws_mqtt_set_interruption_handlers_task *set_task = arg;
+ struct aws_mqtt_client_connection_5_impl *adapter = set_task->adapter;
+
+ if (status != AWS_TASK_STATUS_RUN_READY) {
+ goto done;
+ }
+
+ adapter->on_interrupted = set_task->on_interrupted;
+ adapter->on_interrupted_user_data = set_task->on_interrupted_user_data;
+ adapter->on_resumed = set_task->on_resumed;
+ adapter->on_resumed_user_data = set_task->on_resumed_user_data;
+
+done:
+
+ aws_ref_count_release(&adapter->internal_refs);
+
+ aws_mem_release(set_task->allocator, set_task);
+}
+
+static struct aws_mqtt_set_interruption_handlers_task *s_aws_mqtt_set_interruption_handlers_task_new(
+ struct aws_allocator *allocator,
+ struct aws_mqtt_client_connection_5_impl *adapter,
+ aws_mqtt_client_on_connection_interrupted_fn *on_interrupted,
+ void *on_interrupted_user_data,
+ aws_mqtt_client_on_connection_resumed_fn *on_resumed,
+ void *on_resumed_user_data) {
+
+ struct aws_mqtt_set_interruption_handlers_task *set_task =
+ aws_mem_calloc(allocator, 1, sizeof(struct aws_mqtt_set_interruption_handlers_task));
+
+ aws_task_init(
+ &set_task->task, s_set_interruption_handlers_task_fn, (void *)set_task, "SetInterruptionHandlersTask");
+ set_task->allocator = adapter->allocator;
+ set_task->adapter = (struct aws_mqtt_client_connection_5_impl *)aws_ref_count_acquire(&adapter->internal_refs);
+ set_task->on_interrupted = on_interrupted;
+ set_task->on_interrupted_user_data = on_interrupted_user_data;
+ set_task->on_resumed = on_resumed;
+ set_task->on_resumed_user_data = on_resumed_user_data;
+
+ return set_task;
+}
+
+static int s_aws_mqtt_client_connection_5_set_interruption_handlers(
+ void *impl,
+ aws_mqtt_client_on_connection_interrupted_fn *on_interrupted,
+ void *on_interrupted_user_data,
+ aws_mqtt_client_on_connection_resumed_fn *on_resumed,
+ void *on_resumed_user_data) {
+ struct aws_mqtt_client_connection_5_impl *adapter = impl;
+
+ struct aws_mqtt_set_interruption_handlers_task *task = s_aws_mqtt_set_interruption_handlers_task_new(
+ adapter->allocator, adapter, on_interrupted, on_interrupted_user_data, on_resumed, on_resumed_user_data);
+ if (task == NULL) {
+ int error_code = aws_last_error();
+ AWS_LOGF_ERROR(
+ AWS_LS_MQTT5_TO_MQTT3_ADAPTER,
+ "id=%p: failed to create set interruption handlers task, error code %d(%s)",
+ (void *)adapter,
+ error_code,
+ aws_error_debug_str(error_code));
+ return AWS_OP_ERR;
+ }
+
+ aws_event_loop_schedule_task_now(adapter->loop, &task->task);
+
+ return AWS_OP_SUCCESS;
+}
+
+struct aws_mqtt_set_connection_result_handlers_task {
+ struct aws_task task;
+ struct aws_allocator *allocator;
+ struct aws_mqtt_client_connection_5_impl *adapter;
+
+ aws_mqtt_client_on_connection_success_fn *on_connection_success;
+ void *on_connection_success_user_data;
+
+ aws_mqtt_client_on_connection_failure_fn *on_connection_failure;
+ void *on_connection_failure_user_data;
+};
+
+static void s_set_connection_result_handlers_task_fn(struct aws_task *task, void *arg, enum aws_task_status status) {
+ (void)task;
+
+ struct aws_mqtt_set_connection_result_handlers_task *set_task = arg;
+ struct aws_mqtt_client_connection_5_impl *adapter = set_task->adapter;
+
+ if (status != AWS_TASK_STATUS_RUN_READY) {
+ goto done;
+ }
+
+ adapter->on_connection_success = set_task->on_connection_success;
+ adapter->on_connection_success_user_data = set_task->on_connection_success_user_data;
+ adapter->on_connection_failure = set_task->on_connection_failure;
+ adapter->on_connection_failure_user_data = set_task->on_connection_failure_user_data;
+
+done:
+
+ aws_ref_count_release(&adapter->internal_refs);
+
+ aws_mem_release(set_task->allocator, set_task);
+}
+
+static struct aws_mqtt_set_connection_result_handlers_task *s_aws_mqtt_set_connection_result_handlers_task_new(
+ struct aws_allocator *allocator,
+ struct aws_mqtt_client_connection_5_impl *adapter,
+ aws_mqtt_client_on_connection_success_fn *on_connection_success,
+ void *on_connection_success_user_data,
+ aws_mqtt_client_on_connection_failure_fn *on_connection_failure,
+ void *on_connection_failure_user_data) {
+
+ struct aws_mqtt_set_connection_result_handlers_task *set_task =
+ aws_mem_calloc(allocator, 1, sizeof(struct aws_mqtt_set_connection_result_handlers_task));
+
+ aws_task_init(
+ &set_task->task, s_set_connection_result_handlers_task_fn, (void *)set_task, "SetConnectionResultHandlersTask");
+ set_task->allocator = adapter->allocator;
+ set_task->adapter = (struct aws_mqtt_client_connection_5_impl *)aws_ref_count_acquire(&adapter->internal_refs);
+ set_task->on_connection_success = on_connection_success;
+ set_task->on_connection_success_user_data = on_connection_success_user_data;
+ set_task->on_connection_failure = on_connection_failure;
+ set_task->on_connection_failure_user_data = on_connection_failure_user_data;
+
+ return set_task;
+}
+
+static int s_aws_mqtt_client_connection_5_set_connection_result_handlers(
+ void *impl,
+ aws_mqtt_client_on_connection_success_fn *on_connection_success,
+ void *on_connection_success_user_data,
+ aws_mqtt_client_on_connection_failure_fn *on_connection_failure,
+ void *on_connection_failure_user_data) {
+ struct aws_mqtt_client_connection_5_impl *adapter = impl;
+
+ struct aws_mqtt_set_connection_result_handlers_task *task = s_aws_mqtt_set_connection_result_handlers_task_new(
+ adapter->allocator,
+ adapter,
+ on_connection_success,
+ on_connection_success_user_data,
+ on_connection_failure,
+ on_connection_failure_user_data);
+ if (task == NULL) {
+ int error_code = aws_last_error();
+ AWS_LOGF_ERROR(
+ AWS_LS_MQTT5_TO_MQTT3_ADAPTER,
+ "id=%p: failed to create set connection result handlers task, error code %d(%s)",
+ (void *)adapter,
+ error_code,
+ aws_error_debug_str(error_code));
+ return AWS_OP_ERR;
+ }
+
+ aws_event_loop_schedule_task_now(adapter->loop, &task->task);
+
+ return AWS_OP_SUCCESS;
+}
+
+struct aws_mqtt_set_on_closed_handler_task {
+ struct aws_task task;
+ struct aws_allocator *allocator;
+ struct aws_mqtt_client_connection_5_impl *adapter;
+
+ aws_mqtt_client_on_connection_closed_fn *on_closed;
+ void *on_closed_user_data;
+};
+
+static void s_set_on_closed_handler_task_fn(struct aws_task *task, void *arg, enum aws_task_status status) {
+ (void)task;
+
+ struct aws_mqtt_set_on_closed_handler_task *set_task = arg;
+ struct aws_mqtt_client_connection_5_impl *adapter = set_task->adapter;
+ if (status != AWS_TASK_STATUS_RUN_READY) {
+ goto done;
+ }
+
+ adapter->on_closed = set_task->on_closed;
+ adapter->on_closed_user_data = set_task->on_closed_user_data;
+
+done:
+
+ aws_ref_count_release(&adapter->internal_refs);
+
+ aws_mem_release(set_task->allocator, set_task);
+}
+
+static struct aws_mqtt_set_on_closed_handler_task *s_aws_mqtt_set_on_closed_handler_task_new(
+ struct aws_allocator *allocator,
+ struct aws_mqtt_client_connection_5_impl *adapter,
+ aws_mqtt_client_on_connection_closed_fn *on_closed,
+ void *on_closed_user_data) {
+
+ struct aws_mqtt_set_on_closed_handler_task *set_task =
+ aws_mem_calloc(allocator, 1, sizeof(struct aws_mqtt_set_on_closed_handler_task));
+
+ aws_task_init(&set_task->task, s_set_on_closed_handler_task_fn, (void *)set_task, "SetOnClosedHandlerTask");
+ set_task->allocator = adapter->allocator;
+ set_task->adapter = (struct aws_mqtt_client_connection_5_impl *)aws_ref_count_acquire(&adapter->internal_refs);
+ set_task->on_closed = on_closed;
+ set_task->on_closed_user_data = on_closed_user_data;
+
+ return set_task;
+}
+
+static int s_aws_mqtt_client_connection_5_set_on_closed_handler(
+ void *impl,
+ aws_mqtt_client_on_connection_closed_fn *on_closed,
+ void *on_closed_user_data) {
+ struct aws_mqtt_client_connection_5_impl *adapter = impl;
+
+ struct aws_mqtt_set_on_closed_handler_task *task =
+ s_aws_mqtt_set_on_closed_handler_task_new(adapter->allocator, adapter, on_closed, on_closed_user_data);
+ if (task == NULL) {
+ int error_code = aws_last_error();
+ AWS_LOGF_ERROR(
+ AWS_LS_MQTT5_TO_MQTT3_ADAPTER,
+ "id=%p: failed to create set on closed handler task, error code %d(%s)",
+ (void *)adapter,
+ error_code,
+ aws_error_debug_str(error_code));
+ return AWS_OP_ERR;
+ }
+
+ aws_event_loop_schedule_task_now(adapter->loop, &task->task);
+
+ return AWS_OP_SUCCESS;
+}
+
+struct aws_mqtt_set_on_termination_handlers_task {
+ struct aws_task task;
+ struct aws_allocator *allocator;
+ struct aws_mqtt_client_connection_5_impl *adapter;
+
+ aws_mqtt_client_on_connection_termination_fn *on_termination_callback;
+ void *on_termination_ud;
+};
+
+static void s_set_on_termination_handlers_task_fn(struct aws_task *task, void *arg, enum aws_task_status status) {
+ (void)task;
+ struct aws_mqtt_set_on_termination_handlers_task *set_task = arg;
+ struct aws_mqtt_client_connection_5_impl *adapter = set_task->adapter;
+ if (status != AWS_TASK_STATUS_RUN_READY) {
+ goto done;
+ }
+
+ adapter->on_termination = set_task->on_termination_callback;
+ adapter->on_termination_user_data = set_task->on_termination_ud;
+
+done:
+
+ aws_ref_count_release(&adapter->internal_refs);
+
+ aws_mem_release(set_task->allocator, set_task);
+}
+
+static struct aws_mqtt_set_on_termination_handlers_task *s_aws_mqtt_set_on_termination_handler_task_new(
+ struct aws_allocator *allocator,
+ struct aws_mqtt_client_connection_5_impl *adapter,
+ aws_mqtt_client_on_connection_termination_fn *on_termination,
+ void *on_termination_user_data) {
+
+ struct aws_mqtt_set_on_termination_handlers_task *set_task =
+ aws_mem_calloc(allocator, 1, sizeof(struct aws_mqtt_set_on_termination_handlers_task));
+
+ aws_task_init(&set_task->task, s_set_on_termination_handlers_task_fn, (void *)set_task, "SetOnClosedHandlerTask");
+ set_task->allocator = adapter->allocator;
+ set_task->adapter = (struct aws_mqtt_client_connection_5_impl *)aws_ref_count_acquire(&adapter->internal_refs);
+ set_task->on_termination_callback = on_termination;
+ set_task->on_termination_ud = on_termination_user_data;
+
+ return set_task;
+}
+
+static int s_aws_mqtt_client_connection_5_set_termination_handler(
+ void *impl,
+ aws_mqtt_client_on_connection_termination_fn *on_termination,
+ void *on_termination_ud) {
+ struct aws_mqtt_client_connection_5_impl *adapter = impl;
+
+ struct aws_mqtt_set_on_termination_handlers_task *task =
+ s_aws_mqtt_set_on_termination_handler_task_new(adapter->allocator, adapter, on_termination, on_termination_ud);
+ if (task == NULL) {
+ int error_code = aws_last_error();
+ AWS_LOGF_ERROR(
+ AWS_LS_MQTT5_TO_MQTT3_ADAPTER,
+ "id=%p: failed to create set on closed handler task, error code %d(%s)",
+ (void *)adapter,
+ error_code,
+ aws_error_debug_str(error_code));
+ return AWS_OP_ERR;
+ }
+
+ aws_event_loop_schedule_task_now(adapter->loop, &task->task);
+
+ return AWS_OP_SUCCESS;
+}
+
+struct aws_mqtt_set_on_any_publish_handler_task {
+ struct aws_task task;
+ struct aws_allocator *allocator;
+ struct aws_mqtt_client_connection_5_impl *adapter;
+
+ aws_mqtt_client_publish_received_fn *on_any_publish;
+ void *on_any_publish_user_data;
+};
+
+static void s_set_on_any_publish_handler_task_fn(struct aws_task *task, void *arg, enum aws_task_status status) {
+ (void)task;
+
+ struct aws_mqtt_set_on_any_publish_handler_task *set_task = arg;
+ struct aws_mqtt_client_connection_5_impl *adapter = set_task->adapter;
+ if (status != AWS_TASK_STATUS_RUN_READY) {
+ goto done;
+ }
+
+ adapter->on_any_publish = set_task->on_any_publish;
+ adapter->on_any_publish_user_data = set_task->on_any_publish_user_data;
+
+done:
+
+ aws_ref_count_release(&adapter->internal_refs);
+
+ aws_mem_release(set_task->allocator, set_task);
+}
+
+static struct aws_mqtt_set_on_any_publish_handler_task *s_aws_mqtt_set_on_any_publish_handler_task_new(
+ struct aws_allocator *allocator,
+ struct aws_mqtt_client_connection_5_impl *adapter,
+ aws_mqtt_client_publish_received_fn *on_any_publish,
+ void *on_any_publish_user_data) {
+
+ struct aws_mqtt_set_on_any_publish_handler_task *set_task =
+ aws_mem_calloc(allocator, 1, sizeof(struct aws_mqtt_set_on_any_publish_handler_task));
+
+ aws_task_init(
+ &set_task->task, s_set_on_any_publish_handler_task_fn, (void *)set_task, "SetOnAnyPublishHandlerTask");
+ set_task->allocator = adapter->allocator;
+ set_task->adapter = (struct aws_mqtt_client_connection_5_impl *)aws_ref_count_acquire(&adapter->internal_refs);
+ set_task->on_any_publish = on_any_publish;
+ set_task->on_any_publish_user_data = on_any_publish_user_data;
+
+ return set_task;
+}
+
+static int s_aws_mqtt_client_connection_5_set_on_any_publish_handler(
+ void *impl,
+ aws_mqtt_client_publish_received_fn *on_any_publish,
+ void *on_any_publish_user_data) {
+ struct aws_mqtt_client_connection_5_impl *adapter = impl;
+
+ struct aws_mqtt_set_on_any_publish_handler_task *task = s_aws_mqtt_set_on_any_publish_handler_task_new(
+ adapter->allocator, adapter, on_any_publish, on_any_publish_user_data);
+ if (task == NULL) {
+ int error_code = aws_last_error();
+ AWS_LOGF_ERROR(
+ AWS_LS_MQTT5_TO_MQTT3_ADAPTER,
+ "id=%p: failed to create set on any publish task, error code %d(%s)",
+ (void *)adapter,
+ error_code,
+ aws_error_debug_str(error_code));
+ return AWS_OP_ERR;
+ }
+
+ aws_event_loop_schedule_task_now(adapter->loop, &task->task);
+
+ return AWS_OP_SUCCESS;
+}
+
+struct aws_mqtt_set_reconnect_timeout_task {
+ struct aws_task task;
+ struct aws_allocator *allocator;
+ struct aws_mqtt_client_connection_5_impl *adapter;
+
+ uint64_t min_timeout;
+ uint64_t max_timeout;
+};
+
+static void s_set_reconnect_timeout_task_fn(struct aws_task *task, void *arg, enum aws_task_status status) {
+ (void)task;
+
+ struct aws_mqtt_set_reconnect_timeout_task *set_task = arg;
+ struct aws_mqtt_client_connection_5_impl *adapter = set_task->adapter;
+ if (status != AWS_TASK_STATUS_RUN_READY) {
+ goto done;
+ }
+
+ /* we're in the mqtt5 client's event loop; it's safe to access internal state */
+ adapter->client->config->min_reconnect_delay_ms = set_task->min_timeout;
+ adapter->client->config->max_reconnect_delay_ms = set_task->max_timeout;
+ adapter->client->current_reconnect_delay_ms = set_task->min_timeout;
+
+done:
+
+ aws_ref_count_release(&adapter->internal_refs);
+
+ aws_mem_release(set_task->allocator, set_task);
+}
+
+static struct aws_mqtt_set_reconnect_timeout_task *s_aws_mqtt_set_reconnect_timeout_task_new(
+ struct aws_allocator *allocator,
+ struct aws_mqtt_client_connection_5_impl *adapter,
+ uint64_t min_timeout,
+ uint64_t max_timeout) {
+
+ struct aws_mqtt_set_reconnect_timeout_task *set_task =
+ aws_mem_calloc(allocator, 1, sizeof(struct aws_mqtt_set_reconnect_timeout_task));
+
+ aws_task_init(&set_task->task, s_set_reconnect_timeout_task_fn, (void *)set_task, "SetReconnectTimeoutTask");
+ set_task->allocator = adapter->allocator;
+ set_task->adapter = (struct aws_mqtt_client_connection_5_impl *)aws_ref_count_acquire(&adapter->internal_refs);
+ set_task->min_timeout = aws_min_u64(min_timeout, max_timeout);
+ set_task->max_timeout = aws_max_u64(min_timeout, max_timeout);
+
+ return set_task;
+}
+
+static int s_aws_mqtt_client_connection_5_set_reconnect_timeout(
+ void *impl,
+ uint64_t min_timeout,
+ uint64_t max_timeout) {
+ struct aws_mqtt_client_connection_5_impl *adapter = impl;
+
+ struct aws_mqtt_set_reconnect_timeout_task *task =
+ s_aws_mqtt_set_reconnect_timeout_task_new(adapter->allocator, adapter, min_timeout, max_timeout);
+ if (task == NULL) {
+ int error_code = aws_last_error();
+ AWS_LOGF_ERROR(
+ AWS_LS_MQTT5_TO_MQTT3_ADAPTER,
+ "id=%p: failed to create set reconnect timeout task, error code %d(%s)",
+ (void *)adapter,
+ error_code,
+ aws_error_debug_str(error_code));
+ return AWS_OP_ERR;
+ }
+
+ aws_event_loop_schedule_task_now(adapter->loop, &task->task);
+
+ return AWS_OP_SUCCESS;
+}
+
+struct aws_mqtt_set_http_proxy_options_task {
+ struct aws_task task;
+ struct aws_allocator *allocator;
+ struct aws_mqtt_client_connection_5_impl *adapter;
+
+ struct aws_http_proxy_config *proxy_config;
+};
+
+static void s_set_http_proxy_options_task_fn(struct aws_task *task, void *arg, enum aws_task_status status) {
+ (void)task;
+
+ struct aws_mqtt_set_http_proxy_options_task *set_task = arg;
+ struct aws_mqtt_client_connection_5_impl *adapter = set_task->adapter;
+ if (status != AWS_TASK_STATUS_RUN_READY) {
+ goto done;
+ }
+
+ /* we're in the mqtt5 client's event loop; it's safe to access internal state */
+ aws_http_proxy_config_destroy(adapter->client->config->http_proxy_config);
+
+ /* move the proxy config from the set task to the client's config */
+ adapter->client->config->http_proxy_config = set_task->proxy_config;
+ if (adapter->client->config->http_proxy_config != NULL) {
+ aws_http_proxy_options_init_from_config(
+ &adapter->client->config->http_proxy_options, adapter->client->config->http_proxy_config);
+ }
+
+ /* don't clean up the proxy config if it was successfully assigned to the mqtt5 client */
+ set_task->proxy_config = NULL;
+
+done:
+
+ aws_ref_count_release(&adapter->internal_refs);
+
+ /* If the task was canceled we need to clean this up because it didn't get assigned to the mqtt5 client */
+ aws_http_proxy_config_destroy(set_task->proxy_config);
+
+ aws_mem_release(set_task->allocator, set_task);
+}
+
+static struct aws_mqtt_set_http_proxy_options_task *s_aws_mqtt_set_http_proxy_options_task_new(
+ struct aws_allocator *allocator,
+ struct aws_mqtt_client_connection_5_impl *adapter,
+ struct aws_http_proxy_options *proxy_options) {
+
+ struct aws_http_proxy_config *proxy_config =
+ aws_http_proxy_config_new_tunneling_from_proxy_options(allocator, proxy_options);
+ if (proxy_config == NULL) {
+ aws_raise_error(AWS_ERROR_INVALID_ARGUMENT);
+ return NULL;
+ }
+
+ struct aws_mqtt_set_http_proxy_options_task *set_task =
+ aws_mem_calloc(allocator, 1, sizeof(struct aws_mqtt_set_http_proxy_options_task));
+
+ aws_task_init(&set_task->task, s_set_http_proxy_options_task_fn, (void *)set_task, "SetHttpProxyOptionsTask");
+ set_task->allocator = adapter->allocator;
+ set_task->adapter = (struct aws_mqtt_client_connection_5_impl *)aws_ref_count_acquire(&adapter->internal_refs);
+ set_task->proxy_config = proxy_config;
+
+ return set_task;
+}
+
+static int s_aws_mqtt_client_connection_5_set_http_proxy_options(
+ void *impl,
+ struct aws_http_proxy_options *proxy_options) {
+
+ struct aws_mqtt_client_connection_5_impl *adapter = impl;
+
+ struct aws_mqtt_set_http_proxy_options_task *task =
+ s_aws_mqtt_set_http_proxy_options_task_new(adapter->allocator, adapter, proxy_options);
+ if (task == NULL) {
+ int error_code = aws_last_error();
+ AWS_LOGF_ERROR(
+ AWS_LS_MQTT5_TO_MQTT3_ADAPTER,
+ "id=%p: failed to create set http proxy options task, error code %d(%s)",
+ (void *)adapter,
+ error_code,
+ aws_error_debug_str(error_code));
+ return AWS_OP_ERR;
+ }
+
+ aws_event_loop_schedule_task_now(adapter->loop, &task->task);
+
+ return AWS_OP_SUCCESS;
+}
+
+struct aws_mqtt_set_use_websockets_task {
+ struct aws_task task;
+ struct aws_allocator *allocator;
+ struct aws_mqtt_client_connection_5_impl *adapter;
+
+ aws_mqtt_transform_websocket_handshake_fn *transformer;
+ void *transformer_user_data;
+};
+
+static void s_aws_mqtt5_adapter_websocket_handshake_completion_fn(
+ struct aws_http_message *request,
+ int error_code,
+ void *complete_ctx) {
+
+ struct aws_mqtt_client_connection_5_impl *adapter = complete_ctx;
+
+ (*adapter->mqtt5_websocket_handshake_completion_function)(
+ request,
+ s_translate_mqtt5_error_code_to_mqtt311(error_code),
+ adapter->mqtt5_websocket_handshake_completion_user_data);
+
+ aws_ref_count_release(&adapter->internal_refs);
+}
+
+static void s_aws_mqtt5_adapter_transform_websocket_handshake_fn(
+ struct aws_http_message *request,
+ void *user_data,
+ aws_mqtt5_transform_websocket_handshake_complete_fn *complete_fn,
+ void *complete_ctx) {
+
+ struct aws_mqtt_client_connection_5_impl *adapter = user_data;
+
+ if (adapter->websocket_handshake_transformer == NULL) {
+ (*complete_fn)(request, AWS_ERROR_SUCCESS, complete_ctx);
+ } else {
+ aws_ref_count_acquire(&adapter->internal_refs);
+ adapter->mqtt5_websocket_handshake_completion_function = complete_fn;
+ adapter->mqtt5_websocket_handshake_completion_user_data = complete_ctx;
+
+ (*adapter->websocket_handshake_transformer)(
+ request,
+ adapter->websocket_handshake_transformer_user_data,
+ s_aws_mqtt5_adapter_websocket_handshake_completion_fn,
+ adapter);
+ }
+}
+
+static void s_set_use_websockets_task_fn(struct aws_task *task, void *arg, enum aws_task_status status) {
+ (void)task;
+
+ struct aws_mqtt_set_use_websockets_task *set_task = arg;
+ struct aws_mqtt_client_connection_5_impl *adapter = set_task->adapter;
+ if (status != AWS_TASK_STATUS_RUN_READY) {
+ goto done;
+ }
+
+ adapter->websocket_handshake_transformer = set_task->transformer;
+ adapter->websocket_handshake_transformer_user_data = set_task->transformer_user_data;
+
+ /* we're in the mqtt5 client's event loop; it's safe to access its internal state */
+ adapter->client->config->websocket_handshake_transform = s_aws_mqtt5_adapter_transform_websocket_handshake_fn;
+ adapter->client->config->websocket_handshake_transform_user_data = adapter;
+
+done:
+
+ aws_ref_count_release(&adapter->internal_refs);
+
+ aws_mem_release(set_task->allocator, set_task);
+}
+
+static struct aws_mqtt_set_use_websockets_task *s_aws_mqtt_set_use_websockets_task_new(
+ struct aws_allocator *allocator,
+ struct aws_mqtt_client_connection_5_impl *adapter,
+ aws_mqtt_transform_websocket_handshake_fn *transformer,
+ void *transformer_user_data) {
+
+ struct aws_mqtt_set_use_websockets_task *set_task =
+ aws_mem_calloc(allocator, 1, sizeof(struct aws_mqtt_set_use_websockets_task));
+
+ aws_task_init(&set_task->task, s_set_use_websockets_task_fn, (void *)set_task, "SetUseWebsocketsTask");
+ set_task->allocator = adapter->allocator;
+ set_task->adapter = (struct aws_mqtt_client_connection_5_impl *)aws_ref_count_acquire(&adapter->internal_refs);
+ set_task->transformer = transformer;
+ set_task->transformer_user_data = transformer_user_data;
+
+ return set_task;
+}
+
+static int s_aws_mqtt_client_connection_5_use_websockets(
+ void *impl,
+ aws_mqtt_transform_websocket_handshake_fn *transformer,
+ void *transformer_user_data,
+ aws_mqtt_validate_websocket_handshake_fn *validator,
+ void *validator_user_data) {
+
+ /* mqtt5 doesn't use these */
+ (void)validator;
+ (void)validator_user_data;
+
+ struct aws_mqtt_client_connection_5_impl *adapter = impl;
+
+ struct aws_mqtt_set_use_websockets_task *task =
+ s_aws_mqtt_set_use_websockets_task_new(adapter->allocator, adapter, transformer, transformer_user_data);
+ if (task == NULL) {
+ int error_code = aws_last_error();
+ AWS_LOGF_ERROR(
+ AWS_LS_MQTT5_TO_MQTT3_ADAPTER,
+ "id=%p: failed to create set use websockets task, error code %d(%s)",
+ (void *)adapter,
+ error_code,
+ aws_error_debug_str(error_code));
+ return AWS_OP_ERR;
+ }
+
+ aws_event_loop_schedule_task_now(adapter->loop, &task->task);
+
+ return AWS_OP_SUCCESS;
+}
+
+struct aws_mqtt_set_host_resolution_task {
+ struct aws_task task;
+ struct aws_allocator *allocator;
+ struct aws_mqtt_client_connection_5_impl *adapter;
+
+ struct aws_host_resolution_config host_resolution_config;
+};
+
+static void s_set_host_resolution_task_fn(struct aws_task *task, void *arg, enum aws_task_status status) {
+ (void)task;
+
+ struct aws_mqtt_set_host_resolution_task *set_task = arg;
+ struct aws_mqtt_client_connection_5_impl *adapter = set_task->adapter;
+ if (status != AWS_TASK_STATUS_RUN_READY) {
+ goto done;
+ }
+
+ /* we're in the mqtt5 client's event loop; it's safe to access internal state */
+ adapter->client->config->host_resolution_override = set_task->host_resolution_config;
+
+done:
+
+ aws_ref_count_release(&adapter->internal_refs);
+
+ aws_mem_release(set_task->allocator, set_task);
+}
+
+static struct aws_mqtt_set_host_resolution_task *s_aws_mqtt_set_host_resolution_task_new(
+ struct aws_allocator *allocator,
+ struct aws_mqtt_client_connection_5_impl *adapter,
+ const struct aws_host_resolution_config *host_resolution_config) {
+
+ struct aws_mqtt_set_host_resolution_task *set_task =
+ aws_mem_calloc(allocator, 1, sizeof(struct aws_mqtt_set_host_resolution_task));
+
+ aws_task_init(&set_task->task, s_set_host_resolution_task_fn, (void *)set_task, "SetHostResolutionTask");
+ set_task->allocator = adapter->allocator;
+ set_task->adapter = (struct aws_mqtt_client_connection_5_impl *)aws_ref_count_acquire(&adapter->internal_refs);
+ set_task->host_resolution_config = *host_resolution_config;
+
+ return set_task;
+}
+
+static int s_aws_mqtt_client_connection_5_set_host_resolution_options(
+ void *impl,
+ const struct aws_host_resolution_config *host_resolution_config) {
+
+ struct aws_mqtt_client_connection_5_impl *adapter = impl;
+
+ struct aws_mqtt_set_host_resolution_task *task =
+ s_aws_mqtt_set_host_resolution_task_new(adapter->allocator, adapter, host_resolution_config);
+ if (task == NULL) {
+ int error_code = aws_last_error();
+ AWS_LOGF_ERROR(
+ AWS_LS_MQTT5_TO_MQTT3_ADAPTER,
+ "id=%p: failed to create set reconnect timeout task, error code %d(%s)",
+ (void *)adapter,
+ error_code,
+ aws_error_debug_str(error_code));
+ return AWS_OP_ERR;
+ }
+
+ aws_event_loop_schedule_task_now(adapter->loop, &task->task);
+
+ return AWS_OP_SUCCESS;
+}
+
+struct aws_mqtt_set_will_task {
+ struct aws_task task;
+ struct aws_allocator *allocator;
+ struct aws_mqtt_client_connection_5_impl *adapter;
+
+ struct aws_byte_buf topic_buffer;
+ enum aws_mqtt_qos qos;
+ bool retain;
+ struct aws_byte_buf payload_buffer;
+};
+
+static void s_aws_mqtt_set_will_task_destroy(struct aws_mqtt_set_will_task *task) {
+ if (task == NULL) {
+ return;
+ }
+
+ aws_byte_buf_clean_up(&task->topic_buffer);
+ aws_byte_buf_clean_up(&task->payload_buffer);
+
+ aws_mem_release(task->allocator, task);
+}
+
+static void s_set_will_task_fn(struct aws_task *task, void *arg, enum aws_task_status status) {
+ (void)task;
+
+ struct aws_mqtt_set_will_task *set_task = arg;
+ struct aws_mqtt_client_connection_5_impl *adapter = set_task->adapter;
+ if (status != AWS_TASK_STATUS_RUN_READY) {
+ goto done;
+ }
+
+ /* we're in the mqtt5 client's event loop; it's safe to access internal state */
+ struct aws_mqtt5_packet_connect_storage *connect = adapter->client->config->connect;
+
+ /* clean up the old will if necessary */
+ if (connect->will != NULL) {
+ aws_mqtt5_packet_publish_storage_clean_up(connect->will);
+ aws_mem_release(connect->allocator, connect->will);
+ connect->will = NULL;
+ }
+
+ struct aws_mqtt5_packet_publish_view will = {
+ .topic = aws_byte_cursor_from_buf(&set_task->topic_buffer),
+ .qos = (enum aws_mqtt5_qos)set_task->qos,
+ .retain = set_task->retain,
+ .payload = aws_byte_cursor_from_buf(&set_task->payload_buffer),
+ };
+
+ /* make a new will */
+ connect->will = aws_mem_calloc(connect->allocator, 1, sizeof(struct aws_mqtt5_packet_publish_storage));
+ aws_mqtt5_packet_publish_storage_init(connect->will, connect->allocator, &will);
+
+ /* manually update the storage view's will reference */
+ connect->storage_view.will = &connect->will->storage_view;
+
+done:
+
+ aws_ref_count_release(&adapter->internal_refs);
+
+ s_aws_mqtt_set_will_task_destroy(set_task);
+}
+
+static struct aws_mqtt_set_will_task *s_aws_mqtt_set_will_task_new(
+ struct aws_allocator *allocator,
+ struct aws_mqtt_client_connection_5_impl *adapter,
+ const struct aws_byte_cursor *topic,
+ enum aws_mqtt_qos qos,
+ bool retain,
+ const struct aws_byte_cursor *payload) {
+
+ if (topic == NULL) {
+ return NULL;
+ }
+
+ struct aws_mqtt_set_will_task *set_task = aws_mem_calloc(allocator, 1, sizeof(struct aws_mqtt_set_will_task));
+
+ aws_task_init(&set_task->task, s_set_will_task_fn, (void *)set_task, "SetWillTask");
+ set_task->allocator = adapter->allocator;
+ set_task->adapter = (struct aws_mqtt_client_connection_5_impl *)aws_ref_count_acquire(&adapter->internal_refs);
+
+ set_task->qos = qos;
+ set_task->retain = retain;
+ aws_byte_buf_init_copy_from_cursor(&set_task->topic_buffer, allocator, *topic);
+ if (payload != NULL) {
+ aws_byte_buf_init_copy_from_cursor(&set_task->payload_buffer, allocator, *payload);
+ }
+
+ return set_task;
+}
+
+static int s_aws_mqtt_client_connection_5_set_will(
+ void *impl,
+ const struct aws_byte_cursor *topic,
+ enum aws_mqtt_qos qos,
+ bool retain,
+ const struct aws_byte_cursor *payload) {
+
+ struct aws_mqtt_client_connection_5_impl *adapter = impl;
+
+ /* check qos */
+ if (qos < 0 || qos > AWS_MQTT_QOS_EXACTLY_ONCE) {
+ AWS_LOGF_ERROR(
+ AWS_LS_MQTT5_TO_MQTT3_ADAPTER, "id=%p: mqtt3-to-5-adapter, invalid qos for will", (void *)adapter);
+ return aws_raise_error(AWS_ERROR_MQTT_INVALID_QOS);
+ }
+
+ /* check topic */
+ if (!aws_mqtt_is_valid_topic(topic)) {
+ AWS_LOGF_ERROR(
+ AWS_LS_MQTT5_TO_MQTT3_ADAPTER, "id=%p: mqtt3-to-5-adapter, invalid topic for will", (void *)adapter);
+ return aws_raise_error(AWS_ERROR_MQTT_INVALID_TOPIC);
+ }
+
+ struct aws_mqtt_set_will_task *task =
+ s_aws_mqtt_set_will_task_new(adapter->allocator, adapter, topic, qos, retain, payload);
+ if (task == NULL) {
+ AWS_LOGF_ERROR(AWS_LS_MQTT5_TO_MQTT3_ADAPTER, "id=%p: failed to create set will task", (void *)adapter);
+ return AWS_OP_ERR;
+ }
+
+ aws_event_loop_schedule_task_now(adapter->loop, &task->task);
+
+ return AWS_OP_SUCCESS;
+}
+
+struct aws_mqtt_set_login_task {
+ struct aws_task task;
+ struct aws_allocator *allocator;
+ struct aws_mqtt_client_connection_5_impl *adapter;
+
+ struct aws_byte_buf username_buffer;
+ struct aws_byte_buf password_buffer;
+};
+
+static void s_aws_mqtt_set_login_task_destroy(struct aws_mqtt_set_login_task *task) {
+ if (task == NULL) {
+ return;
+ }
+
+ aws_byte_buf_clean_up_secure(&task->username_buffer);
+ aws_byte_buf_clean_up_secure(&task->password_buffer);
+
+ aws_mem_release(task->allocator, task);
+}
+
+static void s_set_login_task_fn(struct aws_task *task, void *arg, enum aws_task_status status) {
+ (void)task;
+
+ struct aws_mqtt_set_login_task *set_task = arg;
+ struct aws_mqtt_client_connection_5_impl *adapter = set_task->adapter;
+ if (status != AWS_TASK_STATUS_RUN_READY) {
+ goto done;
+ }
+
+ struct aws_byte_cursor username_cursor = aws_byte_cursor_from_buf(&set_task->username_buffer);
+ struct aws_byte_cursor password_cursor = aws_byte_cursor_from_buf(&set_task->password_buffer);
+
+ /* we're in the mqtt5 client's event loop; it's safe to access internal state */
+ struct aws_mqtt5_packet_connect_storage *old_connect = adapter->client->config->connect;
+
+ /*
+ * Packet storage stores binary data in a single buffer. The safest way to replace some binary data is
+ * to make a new storage from the old storage, deleting the old storage after construction is complete.
+ */
+ struct aws_mqtt5_packet_connect_view new_connect_view = old_connect->storage_view;
+
+ if (set_task->username_buffer.len > 0) {
+ new_connect_view.username = &username_cursor;
+ } else {
+ new_connect_view.username = NULL;
+ }
+
+ if (set_task->password_buffer.len > 0) {
+ new_connect_view.password = &password_cursor;
+ } else {
+ new_connect_view.password = NULL;
+ }
+
+ if (aws_mqtt5_packet_connect_view_validate(&new_connect_view)) {
+ AWS_LOGF_ERROR(
+ AWS_LS_MQTT5_TO_MQTT3_ADAPTER,
+ "id=%p: mqtt3-to-5-adapter - invalid CONNECT username or password",
+ (void *)adapter);
+ goto done;
+ }
+
+ struct aws_mqtt5_packet_connect_storage *new_connect =
+ aws_mem_calloc(adapter->allocator, 1, sizeof(struct aws_mqtt5_packet_connect_storage));
+ aws_mqtt5_packet_connect_storage_init(new_connect, adapter->allocator, &new_connect_view);
+
+ adapter->client->config->connect = new_connect;
+ aws_mqtt5_packet_connect_storage_clean_up(old_connect);
+ aws_mem_release(old_connect->allocator, old_connect);
+
+done:
+
+ aws_ref_count_release(&adapter->internal_refs);
+
+ s_aws_mqtt_set_login_task_destroy(set_task);
+}
+
+static struct aws_mqtt_set_login_task *s_aws_mqtt_set_login_task_new(
+ struct aws_allocator *allocator,
+ struct aws_mqtt_client_connection_5_impl *adapter,
+ const struct aws_byte_cursor *username,
+ const struct aws_byte_cursor *password) {
+
+ struct aws_mqtt_set_login_task *set_task = aws_mem_calloc(allocator, 1, sizeof(struct aws_mqtt_set_login_task));
+
+ aws_task_init(&set_task->task, s_set_login_task_fn, (void *)set_task, "SetLoginTask");
+ set_task->allocator = adapter->allocator;
+ set_task->adapter = (struct aws_mqtt_client_connection_5_impl *)aws_ref_count_acquire(&adapter->internal_refs);
+
+ if (username != NULL) {
+ aws_byte_buf_init_copy_from_cursor(&set_task->username_buffer, allocator, *username);
+ }
+
+ if (password != NULL) {
+ aws_byte_buf_init_copy_from_cursor(&set_task->password_buffer, allocator, *password);
+ }
+
+ return set_task;
+}
+
+static int s_aws_mqtt_client_connection_5_set_login(
+ void *impl,
+ const struct aws_byte_cursor *username,
+ const struct aws_byte_cursor *password) {
+
+ struct aws_mqtt_client_connection_5_impl *adapter = impl;
+
+ struct aws_mqtt_set_login_task *task =
+ s_aws_mqtt_set_login_task_new(adapter->allocator, adapter, username, password);
+ if (task == NULL) {
+ int error_code = aws_last_error();
+ AWS_LOGF_ERROR(
+ AWS_LS_MQTT5_TO_MQTT3_ADAPTER,
+ "id=%p: failed to create set login task, error code %d(%s)",
+ (void *)adapter,
+ error_code,
+ aws_error_debug_str(error_code));
+ return AWS_OP_ERR;
+ }
+
+ aws_event_loop_schedule_task_now(adapter->loop, &task->task);
+
+ return AWS_OP_SUCCESS;
+}
+
+static void s_aws_mqtt5_to_mqtt3_adapter_on_zero_internal_refs(void *context) {
+ struct aws_mqtt_client_connection_5_impl *adapter = context;
+
+ s_aws_mqtt_adapter_final_destroy(adapter);
+}
+
+static void s_aws_mqtt5_to_mqtt3_adapter_on_listener_detached(void *context) {
+ struct aws_mqtt_client_connection_5_impl *adapter = context;
+
+ /*
+ * Release the single internal reference that we started with. Only ephemeral references for cross-thread
+ * tasks might remain, and they will disappear quickly.
+ */
+ aws_ref_count_release(&adapter->internal_refs);
+}
+
+static struct aws_mqtt_client_connection *s_aws_mqtt_client_connection_5_acquire(void *impl) {
+ struct aws_mqtt_client_connection_5_impl *adapter = impl;
+
+ aws_ref_count_acquire(&adapter->external_refs);
+
+ return &adapter->base;
+}
+
+static void s_aws_mqtt5_to_mqtt3_adapter_on_zero_external_refs(void *impl) {
+ struct aws_mqtt_client_connection_5_impl *adapter = impl;
+
+ /*
+ * When the adapter's exernal ref count goes to zero, here's what we want to do:
+ *
+ * (1) Release the client listener, starting its asynchronous shutdown process (since we're the only user
+ * of it)
+ * (2) Wait for the client listener to notify us that asynchronous shutdown is over. At this point we
+ * are guaranteed that no more callbacks from the mqtt5 client will reach us.
+ * (3) Release the single internal ref we started with when the adapter was created.
+ * (4) On last internal ref, we can safely release the mqtt5 client and synchronously clean up all other
+ * resources
+ *
+ * Step (1) is done here.
+ * Steps (2) and (3) are accomplished by s_aws_mqtt5_to_mqtt3_adapter_on_listener_detached
+ * Step (4) is completed by s_aws_mqtt5_to_mqtt3_adapter_on_zero_internal_refs
+ */
+ aws_mqtt5_listener_release(adapter->listener);
+}
+
+static void s_aws_mqtt_client_connection_5_release(void *impl) {
+ struct aws_mqtt_client_connection_5_impl *adapter = impl;
+
+ aws_ref_count_release(&adapter->external_refs);
+}
+
+/*
+ * When submitting an operation (across threads), we not only need to keep the adapter alive, we also need to keep
+ * the operation alive since it's technically already being tracked within the adapter's operational state.
+ *
+ * Note: we may not truly need the operation ref but it's safer to keep it.
+ */
+static void s_aws_mqtt5_to_mqtt3_adapter_operation_acquire_cross_thread_refs(
+ struct aws_mqtt5_to_mqtt3_adapter_operation_base *operation) {
+ if (!operation->holding_adapter_ref) {
+ operation->holding_adapter_ref = true;
+ aws_ref_count_acquire(&operation->adapter->internal_refs);
+ }
+
+ aws_mqtt5_to_mqtt3_adapter_operation_acquire(operation);
+}
+
+/*
+ * Once an operation has been received on the adapter's event loop, whether reject or accepted, we must release the
+ * transient references to the operation and adapter
+ */
+static void s_aws_mqtt5_to_mqtt3_adapter_operation_release_cross_thread_refs(
+ struct aws_mqtt5_to_mqtt3_adapter_operation_base *operation) {
+ if (operation->holding_adapter_ref) {
+ operation->holding_adapter_ref = false;
+ aws_ref_count_release(&operation->adapter->internal_refs);
+ }
+
+ aws_mqtt5_to_mqtt3_adapter_operation_release(operation);
+}
+
+static void s_adapter_publish_operation_destroy(void *context) {
+ struct aws_mqtt5_to_mqtt3_adapter_operation_base *operation = context;
+ if (operation == NULL) {
+ return;
+ }
+
+ struct aws_mqtt5_to_mqtt3_adapter_operation_publish *publish_op = operation->impl;
+
+ struct aws_mqtt_client_connection_5_impl *adapter_to_release = NULL;
+ if (publish_op->base.holding_adapter_ref) {
+ adapter_to_release = publish_op->base.adapter;
+ }
+
+ /* We're going away before our MQTT5 operation, make sure it doesn't try to call us back when it completes */
+ publish_op->publish_op->completion_options.completion_callback = NULL;
+ publish_op->publish_op->completion_options.completion_user_data = NULL;
+
+ aws_mqtt5_operation_release(&publish_op->publish_op->base);
+
+ aws_mem_release(operation->allocator, operation);
+
+ if (adapter_to_release != NULL) {
+ aws_ref_count_release(&adapter_to_release->internal_refs);
+ }
+}
+
+static void s_aws_mqtt5_to_mqtt3_adapter_publish_completion_fn(
+ enum aws_mqtt5_packet_type packet_type,
+ const void *packet,
+ int error_code,
+ void *complete_ctx) {
+
+ int error_code_final = error_code;
+
+ if (error_code_final == AWS_ERROR_SUCCESS && packet_type == AWS_MQTT5_PT_PUBACK) {
+ const struct aws_mqtt5_packet_puback_view *puback_view = packet;
+ if (puback_view->reason_code >= 128) {
+ error_code_final = AWS_ERROR_MQTT_ACK_REASON_CODE_FAILURE;
+ }
+ }
+
+ struct aws_mqtt5_to_mqtt3_adapter_operation_publish *publish_op = complete_ctx;
+
+ if (publish_op->on_publish_complete != NULL) {
+ (*publish_op->on_publish_complete)(
+ &publish_op->base.adapter->base,
+ publish_op->base.id,
+ error_code_final,
+ publish_op->on_publish_complete_user_data);
+ }
+
+ aws_mqtt5_to_mqtt3_adapter_operation_table_remove_operation(
+ &publish_op->base.adapter->operational_state, publish_op->base.id);
+}
+
+static void s_fail_publish(void *impl, int error_code) {
+ struct aws_mqtt5_to_mqtt3_adapter_operation_publish *publish_op = impl;
+
+ if (publish_op->on_publish_complete != NULL) {
+ (*publish_op->on_publish_complete)(
+ &publish_op->base.adapter->base,
+ publish_op->base.id,
+ error_code,
+ publish_op->on_publish_complete_user_data);
+ }
+}
+
+static struct aws_mqtt5_to_mqtt3_adapter_operation_vtable s_publish_vtable = {
+ .fail_fn = s_fail_publish,
+};
+
+struct aws_mqtt5_to_mqtt3_adapter_operation_publish *aws_mqtt5_to_mqtt3_adapter_operation_new_publish(
+ struct aws_allocator *allocator,
+ const struct aws_mqtt5_to_mqtt3_adapter_publish_options *options) {
+ struct aws_mqtt5_to_mqtt3_adapter_operation_publish *publish_op =
+ aws_mem_calloc(allocator, 1, sizeof(struct aws_mqtt5_to_mqtt3_adapter_operation_publish));
+
+ publish_op->base.allocator = allocator;
+ aws_ref_count_init(&publish_op->base.ref_count, publish_op, s_adapter_publish_operation_destroy);
+ publish_op->base.impl = publish_op;
+ publish_op->base.vtable = &s_publish_vtable;
+ publish_op->base.type = AWS_MQTT5TO3_AOT_PUBLISH;
+ publish_op->base.adapter = options->adapter;
+ publish_op->base.holding_adapter_ref = false;
+
+ struct aws_mqtt5_packet_publish_view publish_view = {
+ .topic = options->topic,
+ .qos = (enum aws_mqtt5_qos)options->qos,
+ .payload = options->payload,
+ .retain = options->retain,
+ };
+
+ struct aws_mqtt5_publish_completion_options publish_completion_options = {
+ .completion_callback = s_aws_mqtt5_to_mqtt3_adapter_publish_completion_fn,
+ .completion_user_data = publish_op,
+ };
+
+ publish_op->publish_op = aws_mqtt5_operation_publish_new(
+ allocator, options->adapter->client, &publish_view, &publish_completion_options);
+ if (publish_op->publish_op == NULL) {
+ goto error;
+ }
+
+ publish_op->on_publish_complete = options->on_complete;
+ publish_op->on_publish_complete_user_data = options->on_complete_userdata;
+
+ return publish_op;
+
+error:
+
+ aws_mqtt5_to_mqtt3_adapter_operation_release(&publish_op->base);
+
+ return NULL;
+}
+
+void s_adapter_publish_submission_fn(struct aws_task *task, void *arg, enum aws_task_status status) {
+ (void)task;
+
+ struct aws_mqtt5_to_mqtt3_adapter_operation_publish *operation = arg;
+
+ struct aws_mqtt_client_connection_5_impl *adapter = operation->base.adapter;
+
+ aws_mqtt5_client_submit_operation_internal(
+ adapter->client, &operation->publish_op->base, status != AWS_TASK_STATUS_RUN_READY);
+
+ /*
+ * The operation has been submitted in-thread. We can release the transient refs (operation, adapter) needed to
+ * keep things alive during the handover
+ */
+ s_aws_mqtt5_to_mqtt3_adapter_operation_release_cross_thread_refs(&operation->base);
+}
+
+static uint16_t s_aws_mqtt_client_connection_5_publish(
+ void *impl,
+ const struct aws_byte_cursor *topic,
+ enum aws_mqtt_qos qos,
+ bool retain,
+ const struct aws_byte_cursor *payload,
+ aws_mqtt_op_complete_fn *on_complete,
+ void *userdata) {
+
+ struct aws_mqtt_client_connection_5_impl *adapter = impl;
+ AWS_LOGF_DEBUG(AWS_LS_MQTT5_TO_MQTT3_ADAPTER, "id=%p: mqtt3-to-5-adapter, invoking publish API", (void *)adapter);
+
+ /* check qos */
+ if (qos < 0 || qos > AWS_MQTT_QOS_EXACTLY_ONCE) {
+ AWS_LOGF_ERROR(
+ AWS_LS_MQTT5_TO_MQTT3_ADAPTER, "id=%p: mqtt3-to-5-adapter, invalid qos for publish", (void *)adapter);
+ aws_raise_error(AWS_ERROR_MQTT_INVALID_QOS);
+ return 0;
+ }
+
+ if (!aws_mqtt_is_valid_topic(topic)) {
+ AWS_LOGF_ERROR(
+ AWS_LS_MQTT5_TO_MQTT3_ADAPTER, "id=%p: mqtt3-to-5-adapter, invalid topic for publish", (void *)adapter);
+ aws_raise_error(AWS_ERROR_MQTT_INVALID_TOPIC);
+ return 0;
+ }
+
+ struct aws_byte_cursor topic_cursor = *topic;
+ struct aws_byte_cursor payload_cursor;
+ AWS_ZERO_STRUCT(payload_cursor);
+ if (payload != NULL) {
+ payload_cursor = *payload;
+ }
+
+ struct aws_mqtt5_to_mqtt3_adapter_publish_options publish_options = {
+ .adapter = adapter,
+ .topic = topic_cursor,
+ .qos = qos,
+ .retain = retain,
+ .payload = payload_cursor,
+ .on_complete = on_complete,
+ .on_complete_userdata = userdata,
+ };
+
+ struct aws_mqtt5_to_mqtt3_adapter_operation_publish *operation =
+ aws_mqtt5_to_mqtt3_adapter_operation_new_publish(adapter->allocator, &publish_options);
+ if (operation == NULL) {
+ return 0;
+ }
+
+ if (aws_mqtt5_to_mqtt3_adapter_operation_table_add_operation(&adapter->operational_state, &operation->base)) {
+ goto error;
+ }
+
+ uint16_t synthetic_id = operation->base.id;
+
+ /*
+ * While in-transit to the adapter event loop, we take refs to both the operation and the adapter so that we
+ * are guaranteed they are still alive when the cross-thread submission task is run.
+ */
+ s_aws_mqtt5_to_mqtt3_adapter_operation_acquire_cross_thread_refs(&operation->base);
+
+ aws_task_init(
+ &operation->base.submission_task,
+ s_adapter_publish_submission_fn,
+ operation,
+ "Mqtt5ToMqtt3AdapterPublishSubmission");
+
+ aws_event_loop_schedule_task_now(adapter->loop, &operation->base.submission_task);
+
+ return synthetic_id;
+
+error:
+
+ aws_mqtt5_to_mqtt3_adapter_operation_release(&operation->base);
+
+ return 0;
+}
+
+static void s_adapter_subscribe_operation_destroy(void *context) {
+ struct aws_mqtt5_to_mqtt3_adapter_operation_base *operation = context;
+ if (operation == NULL) {
+ return;
+ }
+
+ struct aws_mqtt5_to_mqtt3_adapter_operation_subscribe *subscribe_op = operation->impl;
+
+ size_t subscription_count = aws_array_list_length(&subscribe_op->subscriptions);
+ for (size_t i = 0; i < subscription_count; ++i) {
+ struct aws_mqtt_subscription_set_subscription_record *record = NULL;
+ aws_array_list_get_at(&subscribe_op->subscriptions, &record, i);
+
+ aws_mqtt_subscription_set_subscription_record_destroy(record);
+ }
+ aws_array_list_clean_up(&subscribe_op->subscriptions);
+
+ struct aws_mqtt_client_connection_5_impl *adapter_to_release = NULL;
+ if (subscribe_op->base.holding_adapter_ref) {
+ adapter_to_release = subscribe_op->base.adapter;
+ }
+
+ /* We're going away before our MQTT5 operation, make sure it doesn't try to call us back when it completes */
+ if (subscribe_op->subscribe_op != NULL) {
+ subscribe_op->subscribe_op->completion_options.completion_callback = NULL;
+ subscribe_op->subscribe_op->completion_options.completion_user_data = NULL;
+
+ aws_mqtt5_operation_release(&subscribe_op->subscribe_op->base);
+ }
+
+ aws_mem_release(operation->allocator, operation);
+
+ if (adapter_to_release != NULL) {
+ aws_ref_count_release(&adapter_to_release->internal_refs);
+ }
+}
+
+static enum aws_mqtt_qos s_convert_mqtt5_suback_reason_code_to_mqtt3_granted_qos(
+ enum aws_mqtt5_suback_reason_code reason_code) {
+ switch (reason_code) {
+ case AWS_MQTT5_SARC_GRANTED_QOS_0:
+ case AWS_MQTT5_SARC_GRANTED_QOS_1:
+ case AWS_MQTT5_SARC_GRANTED_QOS_2:
+ return (enum aws_mqtt_qos)reason_code;
+
+ default:
+ return AWS_MQTT_QOS_FAILURE;
+ }
+}
+
+static void s_aws_mqtt5_to_mqtt3_adapter_subscribe_completion_helper(
+ struct aws_mqtt5_to_mqtt3_adapter_operation_subscribe *subscribe_op,
+ const struct aws_mqtt5_packet_suback_view *suback,
+ int error_code) {
+
+ struct aws_mqtt_client_connection_5_impl *adapter = subscribe_op->base.adapter;
+ struct aws_mqtt_subscription_set_subscription_record *record = NULL;
+
+ if (subscribe_op->on_suback != NULL) {
+ AWS_LOGF_DEBUG(
+ AWS_LS_MQTT5_TO_MQTT3_ADAPTER,
+ "id=%p: mqtt3-to-5-adapter, completing single-topic subscribe",
+ (void *)adapter);
+
+ struct aws_byte_cursor topic_filter;
+ AWS_ZERO_STRUCT(topic_filter);
+
+ enum aws_mqtt_qos granted_qos = AWS_MQTT_QOS_AT_MOST_ONCE;
+
+ size_t subscription_count = aws_array_list_length(&subscribe_op->subscriptions);
+ if (subscription_count > 0) {
+ aws_array_list_get_at(&subscribe_op->subscriptions, &record, 0);
+ topic_filter = record->subscription_view.topic_filter;
+ }
+
+ if (suback != NULL) {
+ if (suback->reason_code_count > 0) {
+ granted_qos = s_convert_mqtt5_suback_reason_code_to_mqtt3_granted_qos(suback->reason_codes[0]);
+ }
+ } else {
+ granted_qos = AWS_MQTT_QOS_FAILURE;
+ }
+ (*subscribe_op->on_suback)(
+ &adapter->base,
+ subscribe_op->base.id,
+ &topic_filter,
+ granted_qos,
+ error_code,
+ subscribe_op->on_suback_user_data);
+ }
+
+ if (subscribe_op->on_multi_suback != NULL) {
+ AWS_LOGF_DEBUG(
+ AWS_LS_MQTT5_TO_MQTT3_ADAPTER,
+ "id=%p: mqtt3-to-5-adapter, completing multi-topic subscribe",
+ (void *)adapter);
+
+ if (suback == NULL) {
+ (*subscribe_op->on_multi_suback)(
+ &adapter->base, subscribe_op->base.id, NULL, error_code, subscribe_op->on_multi_suback_user_data);
+ } else {
+ AWS_VARIABLE_LENGTH_ARRAY(
+ struct aws_mqtt_topic_subscription, multi_sub_subscription_buf, suback->reason_code_count);
+ AWS_VARIABLE_LENGTH_ARRAY(
+ struct aws_mqtt_topic_subscription *, multi_sub_subscription_ptr_buf, suback->reason_code_count);
+ struct aws_mqtt_topic_subscription *subscription_ptr =
+ (struct aws_mqtt_topic_subscription *)multi_sub_subscription_buf;
+
+ struct aws_array_list multi_sub_list;
+ aws_array_list_init_static(
+ &multi_sub_list,
+ multi_sub_subscription_ptr_buf,
+ suback->reason_code_count,
+ sizeof(struct aws_mqtt_topic_subscription *));
+
+ size_t subscription_count = aws_array_list_length(&subscribe_op->subscriptions);
+
+ for (size_t i = 0; i < suback->reason_code_count; ++i) {
+ struct aws_mqtt_topic_subscription *subscription = subscription_ptr + i;
+ AWS_ZERO_STRUCT(*subscription);
+
+ subscription->qos = s_convert_mqtt5_suback_reason_code_to_mqtt3_granted_qos(suback->reason_codes[i]);
+
+ if (i < subscription_count) {
+ aws_array_list_get_at(&subscribe_op->subscriptions, &record, i);
+
+ subscription->topic = record->subscription_view.topic_filter;
+ subscription->on_publish = record->subscription_view.on_publish_received;
+ subscription->on_publish_ud = record->subscription_view.callback_user_data;
+ subscription->on_cleanup = record->subscription_view.on_cleanup;
+ }
+
+ aws_array_list_push_back(&multi_sub_list, &subscription);
+ }
+ (*subscribe_op->on_multi_suback)(
+ &adapter->base,
+ subscribe_op->base.id,
+ &multi_sub_list,
+ error_code,
+ subscribe_op->on_multi_suback_user_data);
+ }
+ }
+}
+
+static void s_aws_mqtt5_to_mqtt3_adapter_subscribe_completion_fn(
+ const struct aws_mqtt5_packet_suback_view *suback,
+ int error_code,
+ void *complete_ctx) {
+
+ struct aws_mqtt5_to_mqtt3_adapter_operation_subscribe *subscribe_op = complete_ctx;
+ struct aws_mqtt_client_connection_5_impl *adapter = subscribe_op->base.adapter;
+
+ s_aws_mqtt5_to_mqtt3_adapter_subscribe_completion_helper(subscribe_op, suback, error_code);
+
+ aws_mqtt5_to_mqtt3_adapter_operation_table_remove_operation(&adapter->operational_state, subscribe_op->base.id);
+}
+
+static int s_validate_adapter_subscribe_options(
+ size_t subscription_count,
+ struct aws_mqtt_topic_subscription *subscriptions,
+ struct aws_mqtt_client_connection_5_impl *adapter) {
+ for (size_t i = 0; i < subscription_count; ++i) {
+ struct aws_mqtt_topic_subscription *subscription = subscriptions + i;
+
+ /* check qos */
+ if (subscription->qos < 0 || subscription->qos > AWS_MQTT_QOS_EXACTLY_ONCE) {
+ AWS_LOGF_ERROR(
+ AWS_LS_MQTT5_TO_MQTT3_ADAPTER, "id=%p: mqtt3-to-5-adapter, invalid qos for subscribe", (void *)adapter);
+ return aws_raise_error(AWS_ERROR_MQTT_INVALID_QOS);
+ }
+
+ /* check topic */
+ if (!aws_mqtt_is_valid_topic_filter(&subscription->topic)) {
+ AWS_LOGF_ERROR(
+ AWS_LS_MQTT5_TO_MQTT3_ADAPTER,
+ "id=%p: mqtt3-to-5-adapter, invalid topic filter for subscribe",
+ (void *)adapter);
+ return aws_raise_error(AWS_ERROR_MQTT_INVALID_TOPIC);
+ }
+ }
+
+ return AWS_OP_SUCCESS;
+}
+
+static int s_aws_mqtt5_to_mqtt3_adapter_build_subscribe(
+ struct aws_mqtt5_to_mqtt3_adapter_operation_subscribe *subscribe_op,
+ size_t subscription_count,
+ struct aws_mqtt_topic_subscription *subscriptions) {
+ struct aws_allocator *allocator = subscribe_op->base.allocator;
+
+ /* make persistent adapter sub array */
+ aws_array_list_init_dynamic(
+ &subscribe_op->subscriptions,
+ allocator,
+ subscription_count,
+ sizeof(struct aws_mqtt_subscription_set_subscription_record *));
+
+ for (size_t i = 0; i < subscription_count; ++i) {
+ struct aws_mqtt_topic_subscription *subscription_options = &subscriptions[i];
+
+ struct aws_mqtt_subscription_set_subscription_options subscription_record_options = {
+ .topic_filter = subscription_options->topic,
+ .qos = (enum aws_mqtt5_qos)subscription_options->qos,
+ .on_publish_received = subscription_options->on_publish,
+ .callback_user_data = subscription_options->on_publish_ud,
+ .on_cleanup = subscription_options->on_cleanup,
+ };
+ struct aws_mqtt_subscription_set_subscription_record *record =
+ aws_mqtt_subscription_set_subscription_record_new(allocator, &subscription_record_options);
+
+ aws_array_list_push_back(&subscribe_op->subscriptions, &record);
+ }
+
+ /* make temp mqtt5 subscription view array */
+ AWS_VARIABLE_LENGTH_ARRAY(struct aws_mqtt5_subscription_view, mqtt5_subscription_buffer, subscription_count);
+ struct aws_mqtt5_subscription_view *subscription_ptr = mqtt5_subscription_buffer;
+ for (size_t i = 0; i < subscription_count; ++i) {
+ struct aws_mqtt5_subscription_view *subscription = subscription_ptr + i;
+ AWS_ZERO_STRUCT(*subscription);
+
+ subscription->topic_filter = subscriptions[i].topic;
+ subscription->qos = (enum aws_mqtt5_qos)subscriptions[i].qos;
+ }
+
+ struct aws_mqtt5_packet_subscribe_view subscribe_view = {
+ .subscriptions = subscription_ptr,
+ .subscription_count = subscription_count,
+ };
+
+ struct aws_mqtt5_subscribe_completion_options subscribe_completion_options = {
+ .completion_callback = s_aws_mqtt5_to_mqtt3_adapter_subscribe_completion_fn,
+ .completion_user_data = subscribe_op,
+ };
+
+ subscribe_op->subscribe_op = aws_mqtt5_operation_subscribe_new(
+ allocator, subscribe_op->base.adapter->client, &subscribe_view, &subscribe_completion_options);
+
+ if (subscribe_op->subscribe_op == NULL) {
+ /* subscribe options validation will have been raised as the error */
+ return AWS_OP_ERR;
+ }
+
+ return AWS_OP_SUCCESS;
+}
+
+static void s_fail_subscribe(void *impl, int error_code) {
+ struct aws_mqtt5_to_mqtt3_adapter_operation_subscribe *subscribe_op = impl;
+
+ s_aws_mqtt5_to_mqtt3_adapter_subscribe_completion_helper(subscribe_op, NULL, error_code);
+}
+
+static struct aws_mqtt5_to_mqtt3_adapter_operation_vtable s_subscribe_vtable = {
+ .fail_fn = s_fail_subscribe,
+};
+
+struct aws_mqtt5_to_mqtt3_adapter_operation_subscribe *aws_mqtt5_to_mqtt3_adapter_operation_new_subscribe(
+ struct aws_allocator *allocator,
+ const struct aws_mqtt5_to_mqtt3_adapter_subscribe_options *options,
+ struct aws_mqtt_client_connection_5_impl *adapter) {
+
+ if (s_validate_adapter_subscribe_options(options->subscription_count, options->subscriptions, adapter)) {
+ return NULL;
+ }
+
+ struct aws_mqtt5_to_mqtt3_adapter_operation_subscribe *subscribe_op =
+ aws_mem_calloc(allocator, 1, sizeof(struct aws_mqtt5_to_mqtt3_adapter_operation_subscribe));
+
+ subscribe_op->base.allocator = allocator;
+ aws_ref_count_init(&subscribe_op->base.ref_count, subscribe_op, s_adapter_subscribe_operation_destroy);
+ subscribe_op->base.impl = subscribe_op;
+ subscribe_op->base.vtable = &s_subscribe_vtable;
+ subscribe_op->base.type = AWS_MQTT5TO3_AOT_SUBSCRIBE;
+ subscribe_op->base.adapter = options->adapter;
+ subscribe_op->base.holding_adapter_ref = false;
+
+ /*
+ * If we're a regular subscribe, build the mqtt5 operation now. Otherwise, we have to wait until
+ * we're on the event loop thread and it's safe to query the subscription set.
+ */
+ if (options->subscription_count > 0) {
+ if (s_aws_mqtt5_to_mqtt3_adapter_build_subscribe(
+ subscribe_op, options->subscription_count, options->subscriptions)) {
+ goto error;
+ }
+ }
+
+ subscribe_op->on_suback = options->on_suback;
+ subscribe_op->on_suback_user_data = options->on_suback_user_data;
+ subscribe_op->on_multi_suback = options->on_multi_suback;
+ subscribe_op->on_multi_suback_user_data = options->on_multi_suback_user_data;
+
+ return subscribe_op;
+
+error:
+
+ aws_mqtt5_to_mqtt3_adapter_operation_release(&subscribe_op->base);
+
+ return NULL;
+}
+
+static int s_aws_mqtt5_to_mqtt3_adapter_build_resubscribe(
+ struct aws_mqtt5_to_mqtt3_adapter_operation_subscribe *subscribe_op,
+ struct aws_array_list *full_subscriptions) {
+ size_t subscription_count = aws_array_list_length(full_subscriptions);
+
+ AWS_VARIABLE_LENGTH_ARRAY(struct aws_mqtt_topic_subscription, multi_sub_subscriptions, subscription_count);
+
+ for (size_t i = 0; i < subscription_count; ++i) {
+ struct aws_mqtt_subscription_set_subscription_options *existing_subscription = NULL;
+ aws_array_list_get_at_ptr(full_subscriptions, (void **)&existing_subscription, i);
+
+ multi_sub_subscriptions[i].topic = existing_subscription->topic_filter;
+ multi_sub_subscriptions[i].qos = (enum aws_mqtt_qos)existing_subscription->qos;
+ multi_sub_subscriptions[i].on_publish = existing_subscription->on_publish_received;
+ multi_sub_subscriptions[i].on_cleanup = existing_subscription->on_cleanup;
+ multi_sub_subscriptions[i].on_publish_ud = existing_subscription->callback_user_data;
+ }
+
+ return s_aws_mqtt5_to_mqtt3_adapter_build_subscribe(subscribe_op, subscription_count, multi_sub_subscriptions);
+}
+
+void s_adapter_subscribe_submission_fn(struct aws_task *task, void *arg, enum aws_task_status status) {
+ (void)task;
+
+ struct aws_mqtt5_to_mqtt3_adapter_operation_subscribe *operation = arg;
+ struct aws_mqtt_client_connection_5_impl *adapter = operation->base.adapter;
+
+ struct aws_array_list full_subscriptions;
+ AWS_ZERO_STRUCT(full_subscriptions);
+
+ /* If we're a re-subscribe, it's now safe to build the subscription set and MQTT5 subscribe op */
+ if (operation->subscribe_op == NULL) {
+ aws_mqtt_subscription_set_get_subscriptions(adapter->subscriptions, &full_subscriptions);
+ size_t subscription_count = aws_array_list_length(&full_subscriptions);
+ if (subscription_count == 0 || s_aws_mqtt5_to_mqtt3_adapter_build_resubscribe(operation, &full_subscriptions)) {
+ /* There's either nothing to do (no subscriptions) or we failed to build the op (should never happen) */
+ int error_code = aws_last_error();
+ if (subscription_count == 0) {
+ error_code = AWS_ERROR_MQTT_CONNECTION_RESUBSCRIBE_NO_TOPICS;
+ }
+
+ if (operation->on_multi_suback) {
+ (*operation->on_multi_suback)(
+ &adapter->base, operation->base.id, NULL, error_code, operation->on_multi_suback_user_data);
+ }
+
+ /*
+ * Remove the persistent ref represented by being seated in the incomplete operations table.
+ * The other (transient) ref gets released at the end of the function.
+ */
+ aws_mqtt5_to_mqtt3_adapter_operation_table_remove_operation(
+ &adapter->operational_state, operation->base.id);
+ goto complete;
+ }
+ }
+
+ size_t subscription_count = aws_array_list_length(&operation->subscriptions);
+ for (size_t i = 0; i < subscription_count; ++i) {
+ struct aws_mqtt_subscription_set_subscription_record *record = NULL;
+ aws_array_list_get_at(&operation->subscriptions, &record, i);
+
+ aws_mqtt_subscription_set_add_subscription(adapter->subscriptions, &record->subscription_view);
+ }
+
+ aws_mqtt5_client_submit_operation_internal(
+ adapter->client, &operation->subscribe_op->base, status != AWS_TASK_STATUS_RUN_READY);
+
+complete:
+
+ aws_array_list_clean_up(&full_subscriptions);
+
+ /*
+ * The operation has been submitted in-thread. We can release the transient refs (operation, adapter) needed to
+ * keep things alive during the handover
+ */
+ s_aws_mqtt5_to_mqtt3_adapter_operation_release_cross_thread_refs(&operation->base);
+}
+
+static uint16_t s_aws_mqtt_client_connection_5_subscribe(
+ void *impl,
+ const struct aws_byte_cursor *topic_filter,
+ enum aws_mqtt_qos qos,
+ aws_mqtt_client_publish_received_fn *on_publish,
+ void *on_publish_ud,
+ aws_mqtt_userdata_cleanup_fn *on_ud_cleanup,
+ aws_mqtt_suback_fn *on_suback,
+ void *on_suback_user_data) {
+
+ struct aws_mqtt_client_connection_5_impl *adapter = impl;
+ AWS_LOGF_DEBUG(
+ AWS_LS_MQTT5_TO_MQTT3_ADAPTER,
+ "id=%p: mqtt3-to-5-adapter, single-topic subscribe API invoked",
+ (void *)adapter);
+
+ struct aws_mqtt_topic_subscription subscription = {
+ .topic = *topic_filter,
+ .qos = qos,
+ .on_publish = on_publish,
+ .on_cleanup = on_ud_cleanup,
+ .on_publish_ud = on_publish_ud,
+ };
+
+ struct aws_mqtt5_to_mqtt3_adapter_subscribe_options subscribe_options = {
+ .adapter = adapter,
+ .subscriptions = &subscription,
+ .subscription_count = 1,
+ .on_suback = on_suback,
+ .on_suback_user_data = on_suback_user_data,
+ };
+
+ struct aws_mqtt5_to_mqtt3_adapter_operation_subscribe *operation =
+ aws_mqtt5_to_mqtt3_adapter_operation_new_subscribe(adapter->allocator, &subscribe_options, adapter);
+ if (operation == NULL) {
+ return 0;
+ }
+
+ if (aws_mqtt5_to_mqtt3_adapter_operation_table_add_operation(&adapter->operational_state, &operation->base)) {
+ goto error;
+ }
+
+ uint16_t synthetic_id = operation->base.id;
+
+ /*
+ * While in-transit to the adapter event loop, we take refs to both the operation and the adapter so that we
+ * are guaranteed they are still alive when the cross-thread submission task is run.
+ */
+ s_aws_mqtt5_to_mqtt3_adapter_operation_acquire_cross_thread_refs(&operation->base);
+
+ aws_task_init(
+ &operation->base.submission_task,
+ s_adapter_subscribe_submission_fn,
+ operation,
+ "Mqtt5ToMqtt3AdapterSubscribeSubmission");
+
+ aws_event_loop_schedule_task_now(adapter->loop, &operation->base.submission_task);
+
+ return synthetic_id;
+
+error:
+
+ ;
+ int error_code = aws_last_error();
+ AWS_LOGF_ERROR(
+ AWS_LS_MQTT5_TO_MQTT3_ADAPTER,
+ "id=%p: mqtt3-to-5-adapter, single-topic subscribe failed synchronously, error code %d(%s)",
+ (void *)adapter,
+ error_code,
+ aws_error_debug_str(error_code));
+
+ aws_mqtt5_to_mqtt3_adapter_operation_release(&operation->base);
+
+ return 0;
+}
+
+static uint16_t s_aws_mqtt_client_connection_5_subscribe_multiple(
+ void *impl,
+ const struct aws_array_list *topic_filters,
+ aws_mqtt_suback_multi_fn *on_suback,
+ void *on_suback_user_data) {
+
+ struct aws_mqtt_client_connection_5_impl *adapter = impl;
+
+ AWS_LOGF_DEBUG(
+ AWS_LS_MQTT5_TO_MQTT3_ADAPTER, "id=%p: mqtt3-to-5-adapter, multi-topic subscribe API invoked", (void *)adapter);
+
+ if (topic_filters == NULL || aws_array_list_length(topic_filters) == 0) {
+ AWS_LOGF_ERROR(
+ AWS_LS_MQTT5_TO_MQTT3_ADAPTER, "id=%p: mqtt3-to-5-adapter multi-topic subscribe empty", (void *)adapter);
+ aws_raise_error(AWS_ERROR_INVALID_ARGUMENT);
+ return 0;
+ }
+
+ struct aws_mqtt_topic_subscription *subscriptions = topic_filters->data;
+
+ struct aws_mqtt5_to_mqtt3_adapter_subscribe_options subscribe_options = {
+ .adapter = adapter,
+ .subscriptions = subscriptions,
+ .subscription_count = aws_array_list_length(topic_filters),
+ .on_multi_suback = on_suback,
+ .on_multi_suback_user_data = on_suback_user_data,
+ };
+
+ struct aws_mqtt5_to_mqtt3_adapter_operation_subscribe *operation =
+ aws_mqtt5_to_mqtt3_adapter_operation_new_subscribe(adapter->allocator, &subscribe_options, adapter);
+ if (operation == NULL) {
+ int error_code = aws_last_error();
+ AWS_LOGF_ERROR(
+ AWS_LS_MQTT5_TO_MQTT3_ADAPTER,
+ "id=%p: mqtt3-to-5-adapter, multi-topic subscribe operation creation failed, error code %d(%s)",
+ (void *)adapter,
+ error_code,
+ aws_error_debug_str(error_code));
+ return 0;
+ }
+
+ if (aws_mqtt5_to_mqtt3_adapter_operation_table_add_operation(&adapter->operational_state, &operation->base)) {
+ goto error;
+ }
+
+ uint16_t synthetic_id = operation->base.id;
+
+ /*
+ * While in-transit to the adapter event loop, we take refs to both the operation and the adapter so that we
+ * are guaranteed they are still alive when the cross-thread submission task is run.
+ */
+ s_aws_mqtt5_to_mqtt3_adapter_operation_acquire_cross_thread_refs(&operation->base);
+
+ aws_task_init(
+ &operation->base.submission_task,
+ s_adapter_subscribe_submission_fn,
+ operation,
+ "Mqtt5ToMqtt3AdapterSubscribeMultipleSubmission");
+
+ aws_event_loop_schedule_task_now(adapter->loop, &operation->base.submission_task);
+
+ return synthetic_id;
+
+error:
+
+ ;
+ int error_code = aws_last_error();
+ AWS_LOGF_ERROR(
+ AWS_LS_MQTT5_TO_MQTT3_ADAPTER,
+ "id=%p: mqtt3-to-5-adapter, multi-topic subscribe failed, error code %d(%s)",
+ (void *)adapter,
+ error_code,
+ aws_error_debug_str(error_code));
+
+ aws_mqtt5_to_mqtt3_adapter_operation_release(&operation->base);
+
+ return 0;
+}
+
+static void s_adapter_unsubscribe_operation_destroy(void *context) {
+ struct aws_mqtt5_to_mqtt3_adapter_operation_base *operation = context;
+ if (operation == NULL) {
+ return;
+ }
+
+ struct aws_mqtt5_to_mqtt3_adapter_operation_unsubscribe *unsubscribe_op = operation->impl;
+
+ aws_byte_buf_clean_up(&unsubscribe_op->topic_filter);
+
+ struct aws_mqtt_client_connection_5_impl *adapter_to_release = NULL;
+ if (unsubscribe_op->base.holding_adapter_ref) {
+ adapter_to_release = unsubscribe_op->base.adapter;
+ }
+
+ /* We're going away before our MQTT5 operation, make sure it doesn't try to call us back when it completes */
+ unsubscribe_op->unsubscribe_op->completion_options.completion_callback = NULL;
+ unsubscribe_op->unsubscribe_op->completion_options.completion_user_data = NULL;
+
+ aws_mqtt5_operation_release(&unsubscribe_op->unsubscribe_op->base);
+
+ aws_mem_release(operation->allocator, operation);
+
+ if (adapter_to_release != NULL) {
+ aws_ref_count_release(&adapter_to_release->internal_refs);
+ }
+}
+
+static void s_aws_mqtt5_to_mqtt3_adapter_unsubscribe_completion_fn(
+ const struct aws_mqtt5_packet_unsuback_view *unsuback,
+ int error_code,
+ void *complete_ctx) {
+ (void)unsuback;
+
+ struct aws_mqtt5_to_mqtt3_adapter_operation_unsubscribe *unsubscribe_op = complete_ctx;
+
+ if (unsubscribe_op->on_unsuback != NULL) {
+ (*unsubscribe_op->on_unsuback)(
+ &unsubscribe_op->base.adapter->base,
+ unsubscribe_op->base.id,
+ error_code,
+ unsubscribe_op->on_unsuback_user_data);
+ }
+
+ aws_mqtt5_to_mqtt3_adapter_operation_table_remove_operation(
+ &unsubscribe_op->base.adapter->operational_state, unsubscribe_op->base.id);
+}
+
+static void s_fail_unsubscribe(void *impl, int error_code) {
+ struct aws_mqtt5_to_mqtt3_adapter_operation_unsubscribe *unsubscribe_op = impl;
+
+ if (unsubscribe_op->on_unsuback != NULL) {
+ (*unsubscribe_op->on_unsuback)(
+ &unsubscribe_op->base.adapter->base,
+ unsubscribe_op->base.id,
+ error_code,
+ unsubscribe_op->on_unsuback_user_data);
+ }
+}
+
+static struct aws_mqtt5_to_mqtt3_adapter_operation_vtable s_unsubscribe_vtable = {
+ .fail_fn = s_fail_unsubscribe,
+};
+
+struct aws_mqtt5_to_mqtt3_adapter_operation_unsubscribe *aws_mqtt5_to_mqtt3_adapter_operation_new_unsubscribe(
+ struct aws_allocator *allocator,
+ const struct aws_mqtt5_to_mqtt3_adapter_unsubscribe_options *options) {
+
+ struct aws_mqtt5_to_mqtt3_adapter_operation_unsubscribe *unsubscribe_op =
+ aws_mem_calloc(allocator, 1, sizeof(struct aws_mqtt5_to_mqtt3_adapter_operation_unsubscribe));
+
+ unsubscribe_op->base.allocator = allocator;
+ aws_ref_count_init(&unsubscribe_op->base.ref_count, unsubscribe_op, s_adapter_unsubscribe_operation_destroy);
+ unsubscribe_op->base.impl = unsubscribe_op;
+ unsubscribe_op->base.vtable = &s_unsubscribe_vtable;
+ unsubscribe_op->base.type = AWS_MQTT5TO3_AOT_UNSUBSCRIBE;
+ unsubscribe_op->base.adapter = options->adapter;
+ unsubscribe_op->base.holding_adapter_ref = false;
+
+ struct aws_mqtt5_packet_unsubscribe_view unsubscribe_view = {
+ .topic_filters = &options->topic_filter,
+ .topic_filter_count = 1,
+ };
+
+ struct aws_mqtt5_unsubscribe_completion_options unsubscribe_completion_options = {
+ .completion_callback = s_aws_mqtt5_to_mqtt3_adapter_unsubscribe_completion_fn,
+ .completion_user_data = unsubscribe_op,
+ };
+
+ unsubscribe_op->unsubscribe_op = aws_mqtt5_operation_unsubscribe_new(
+ allocator, options->adapter->client, &unsubscribe_view, &unsubscribe_completion_options);
+ if (unsubscribe_op->unsubscribe_op == NULL) {
+ goto error;
+ }
+
+ unsubscribe_op->on_unsuback = options->on_unsuback;
+ unsubscribe_op->on_unsuback_user_data = options->on_unsuback_user_data;
+
+ aws_byte_buf_init_copy_from_cursor(&unsubscribe_op->topic_filter, allocator, options->topic_filter);
+
+ return unsubscribe_op;
+
+error:
+
+ aws_mqtt5_to_mqtt3_adapter_operation_release(&unsubscribe_op->base);
+
+ return NULL;
+}
+
+void s_adapter_unsubscribe_submission_fn(struct aws_task *task, void *arg, enum aws_task_status status) {
+ (void)task;
+
+ struct aws_mqtt5_to_mqtt3_adapter_operation_unsubscribe *operation = arg;
+
+ struct aws_mqtt_client_connection_5_impl *adapter = operation->base.adapter;
+
+ aws_mqtt_subscription_set_remove_subscription(
+ adapter->subscriptions, aws_byte_cursor_from_buf(&operation->topic_filter));
+
+ aws_mqtt5_client_submit_operation_internal(
+ adapter->client, &operation->unsubscribe_op->base, status != AWS_TASK_STATUS_RUN_READY);
+
+ /*
+ * The operation has been submitted in-thread. We can release the transient refs (operation, adapter) needed to
+ * keep things alive during the handover
+ */
+ s_aws_mqtt5_to_mqtt3_adapter_operation_release_cross_thread_refs(&operation->base);
+}
+
+static uint16_t s_aws_mqtt_client_connection_5_unsubscribe(
+ void *impl,
+ const struct aws_byte_cursor *topic_filter,
+ aws_mqtt_op_complete_fn *on_unsuback,
+ void *on_unsuback_user_data) {
+
+ struct aws_mqtt_client_connection_5_impl *adapter = impl;
+
+ AWS_LOGF_DEBUG(AWS_LS_MQTT5_TO_MQTT3_ADAPTER, "id=%p: mqtt3-to-5-adapter, unsubscribe called", (void *)adapter);
+
+ if (!aws_mqtt_is_valid_topic_filter(topic_filter)) {
+ AWS_LOGF_ERROR(
+ AWS_LS_MQTT5_TO_MQTT3_ADAPTER,
+ "id=%p: mqtt3-to-5-adapter, unsubscribe failed, invalid topic filter",
+ (void *)adapter);
+ aws_raise_error(AWS_ERROR_MQTT_INVALID_TOPIC);
+ return 0;
+ }
+
+ struct aws_mqtt5_to_mqtt3_adapter_unsubscribe_options unsubscribe_options = {
+ .adapter = adapter,
+ .topic_filter = *topic_filter,
+ .on_unsuback = on_unsuback,
+ .on_unsuback_user_data = on_unsuback_user_data,
+ };
+
+ struct aws_mqtt5_to_mqtt3_adapter_operation_unsubscribe *operation =
+ aws_mqtt5_to_mqtt3_adapter_operation_new_unsubscribe(adapter->allocator, &unsubscribe_options);
+ if (operation == NULL) {
+ int error_code = aws_last_error();
+ AWS_LOGF_ERROR(
+ AWS_LS_MQTT5_TO_MQTT3_ADAPTER,
+ "id=%p: mqtt3-to-5-adapter, unsubscribe operation creation failed, error code %d(%s)",
+ (void *)adapter,
+ error_code,
+ aws_error_debug_str(error_code));
+ return 0;
+ }
+
+ if (aws_mqtt5_to_mqtt3_adapter_operation_table_add_operation(&adapter->operational_state, &operation->base)) {
+ goto error;
+ }
+
+ uint16_t synthetic_id = operation->base.id;
+
+ /*
+ * While in-transit to the adapter event loop, we take refs to both the operation and the adapter so that we
+ * are guaranteed they are still alive when the cross-thread submission task is run.
+ */
+ s_aws_mqtt5_to_mqtt3_adapter_operation_acquire_cross_thread_refs(&operation->base);
+
+ aws_task_init(
+ &operation->base.submission_task,
+ s_adapter_unsubscribe_submission_fn,
+ operation,
+ "Mqtt5ToMqtt3AdapterUnsubscribeSubmission");
+
+ aws_event_loop_schedule_task_now(adapter->loop, &operation->base.submission_task);
+
+ return synthetic_id;
+
+error:
+
+ ;
+ int error_code = aws_last_error();
+ AWS_LOGF_ERROR(
+ AWS_LS_MQTT5_TO_MQTT3_ADAPTER,
+ "id=%p: mqtt3-to-5-adapter, unsubscribe failed, error code %d(%s)",
+ (void *)adapter,
+ error_code,
+ aws_error_debug_str(error_code));
+
+ aws_mqtt5_to_mqtt3_adapter_operation_release(&operation->base);
+
+ return 0;
+}
+
+static int s_aws_mqtt_client_connection_5_reconnect(
+ void *impl,
+ aws_mqtt_client_on_connection_complete_fn *on_connection_complete,
+ void *userdata) {
+ (void)impl;
+ (void)on_connection_complete;
+ (void)userdata;
+
+ /* DEPRECATED, connection will reconnect automatically now. */
+ AWS_LOGF_ERROR(AWS_LS_MQTT5_TO_MQTT3_ADAPTER, "aws_mqtt_client_connection_reconnect has been DEPRECATED.");
+ return aws_raise_error(AWS_ERROR_UNSUPPORTED_OPERATION);
+}
+
+static int s_aws_mqtt_client_connection_5_get_stats(
+ void *impl,
+ struct aws_mqtt_connection_operation_statistics *stats) {
+
+ struct aws_mqtt_client_connection_5_impl *adapter = impl;
+
+ // Error checking
+ if (!adapter) {
+ AWS_LOGF_ERROR(
+ AWS_LS_MQTT5_TO_MQTT3_ADAPTER, "Invalid MQTT3-to-5 adapter used when trying to get operation statistics");
+ return aws_raise_error(AWS_ERROR_INVALID_ARGUMENT);
+ }
+
+ AWS_LOGF_DEBUG(AWS_LS_MQTT5_TO_MQTT3_ADAPTER, "id=%p: mqtt3-to-5-adapter, get_stats invoked", (void *)adapter);
+
+ if (!stats) {
+ AWS_LOGF_ERROR(
+ AWS_LS_MQTT5_TO_MQTT3_ADAPTER,
+ "id=%p: Invalid MQTT311 statistics struct used when trying to get operation statistics",
+ (void *)adapter);
+ return aws_raise_error(AWS_ERROR_INVALID_ARGUMENT);
+ }
+
+ struct aws_mqtt5_client_operation_statistics mqtt5_stats;
+ AWS_ZERO_STRUCT(mqtt5_stats);
+ aws_mqtt5_client_get_stats(adapter->client, &mqtt5_stats);
+
+ stats->incomplete_operation_count = mqtt5_stats.incomplete_operation_count;
+ stats->incomplete_operation_size = mqtt5_stats.incomplete_operation_size;
+ stats->unacked_operation_count = mqtt5_stats.unacked_operation_count;
+ stats->unacked_operation_size = mqtt5_stats.unacked_operation_size;
+
+ return AWS_OP_SUCCESS;
+}
+
+static uint16_t s_aws_mqtt_5_resubscribe_existing_topics(
+ void *impl,
+ aws_mqtt_suback_multi_fn *on_suback,
+ void *on_suback_user_data) {
+
+ struct aws_mqtt_client_connection_5_impl *adapter = impl;
+
+ AWS_LOGF_DEBUG(
+ AWS_LS_MQTT5_TO_MQTT3_ADAPTER,
+ "id=%p: mqtt3-to-5-adapter, resubscribe_existing_topics invoked",
+ (void *)adapter);
+
+ struct aws_mqtt5_to_mqtt3_adapter_subscribe_options subscribe_options = {
+ .adapter = adapter,
+ .subscriptions = NULL,
+ .subscription_count = 0,
+ .on_multi_suback = on_suback,
+ .on_multi_suback_user_data = on_suback_user_data,
+ };
+
+ struct aws_mqtt5_to_mqtt3_adapter_operation_subscribe *operation =
+ aws_mqtt5_to_mqtt3_adapter_operation_new_subscribe(adapter->allocator, &subscribe_options, adapter);
+ if (operation == NULL) {
+ int error_code = aws_last_error();
+ AWS_LOGF_ERROR(
+ AWS_LS_MQTT5_TO_MQTT3_ADAPTER,
+ "id=%p: mqtt3-to-5-adapter, resubscribe_existing_topics failed on operation creation, error code %d(%s)",
+ (void *)adapter,
+ error_code,
+ aws_error_debug_str(error_code));
+ return 0;
+ }
+
+ if (aws_mqtt5_to_mqtt3_adapter_operation_table_add_operation(&adapter->operational_state, &operation->base)) {
+ goto error;
+ }
+
+ uint16_t synthetic_id = operation->base.id;
+
+ /*
+ * While in-transit to the adapter event loop, we take refs to both the operation and the adapter so that we
+ * are guaranteed they are still alive when the cross-thread submission task is run.
+ */
+ s_aws_mqtt5_to_mqtt3_adapter_operation_acquire_cross_thread_refs(&operation->base);
+
+ aws_task_init(
+ &operation->base.submission_task,
+ s_adapter_subscribe_submission_fn,
+ operation,
+ "Mqtt5ToMqtt3AdapterSubscribeResubscribe");
+
+ aws_event_loop_schedule_task_now(adapter->loop, &operation->base.submission_task);
+
+ return synthetic_id;
+
+error:
+
+ ;
+ int error_code = aws_last_error();
+ AWS_LOGF_ERROR(
+ AWS_LS_MQTT5_TO_MQTT3_ADAPTER,
+ "id=%p: mqtt3-to-5-adapter, resubscribe_existing_topics failed, error code %d(%s)",
+ (void *)adapter,
+ error_code,
+ aws_error_debug_str(error_code));
+
+ aws_mqtt5_to_mqtt3_adapter_operation_release(&operation->base);
+
+ return 0;
+}
+
+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,
+ .set_will_fn = s_aws_mqtt_client_connection_5_set_will,
+ .set_login_fn = s_aws_mqtt_client_connection_5_set_login,
+ .use_websockets_fn = s_aws_mqtt_client_connection_5_use_websockets,
+ .set_http_proxy_options_fn = s_aws_mqtt_client_connection_5_set_http_proxy_options,
+ .set_host_resolution_options_fn = s_aws_mqtt_client_connection_5_set_host_resolution_options,
+ .set_reconnect_timeout_fn = s_aws_mqtt_client_connection_5_set_reconnect_timeout,
+ .set_connection_result_handlers = s_aws_mqtt_client_connection_5_set_connection_result_handlers,
+ .set_connection_interruption_handlers_fn = s_aws_mqtt_client_connection_5_set_interruption_handlers,
+ .set_connection_closed_handler_fn = s_aws_mqtt_client_connection_5_set_on_closed_handler,
+ .set_connection_termination_handler_fn = s_aws_mqtt_client_connection_5_set_termination_handler,
+ .set_on_any_publish_handler_fn = s_aws_mqtt_client_connection_5_set_on_any_publish_handler,
+ .connect_fn = s_aws_mqtt_client_connection_5_connect,
+ .reconnect_fn = s_aws_mqtt_client_connection_5_reconnect,
+ .disconnect_fn = s_aws_mqtt_client_connection_5_disconnect,
+ .subscribe_multiple_fn = s_aws_mqtt_client_connection_5_subscribe_multiple,
+ .subscribe_fn = s_aws_mqtt_client_connection_5_subscribe,
+ .resubscribe_existing_topics_fn = s_aws_mqtt_5_resubscribe_existing_topics,
+ .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,
+};
+
+static struct aws_mqtt_client_connection_vtable *s_aws_mqtt_client_connection_5_vtable_ptr =
+ &s_aws_mqtt_client_connection_5_vtable;
+
+struct aws_mqtt_client_connection *aws_mqtt_client_connection_new_from_mqtt5_client(struct aws_mqtt5_client *client) {
+ struct aws_allocator *allocator = client->allocator;
+ struct aws_mqtt_client_connection_5_impl *adapter =
+ aws_mem_calloc(allocator, 1, sizeof(struct aws_mqtt_client_connection_5_impl));
+
+ adapter->allocator = allocator;
+
+ adapter->base.vtable = s_aws_mqtt_client_connection_5_vtable_ptr;
+ adapter->base.impl = adapter;
+
+ adapter->client = aws_mqtt5_client_acquire(client);
+ adapter->loop = client->loop;
+ adapter->adapter_state = AWS_MQTT_AS_STAY_DISCONNECTED;
+
+ aws_ref_count_init(&adapter->external_refs, adapter, s_aws_mqtt5_to_mqtt3_adapter_on_zero_external_refs);
+ aws_ref_count_init(&adapter->internal_refs, adapter, s_aws_mqtt5_to_mqtt3_adapter_on_zero_internal_refs);
+
+ aws_mqtt5_to_mqtt3_adapter_operation_table_init(&adapter->operational_state, allocator);
+
+ adapter->subscriptions = aws_mqtt_subscription_set_new(allocator);
+
+ struct aws_mqtt5_listener_config listener_config = {
+ .client = client,
+ .listener_callbacks =
+ {
+ .listener_publish_received_handler = s_aws_mqtt5_listener_publish_received_adapter,
+ .listener_publish_received_handler_user_data = adapter,
+ .lifecycle_event_handler = s_aws_mqtt5_to_mqtt3_adapter_lifecycle_handler,
+ .lifecycle_event_handler_user_data = adapter,
+ },
+ .termination_callback = s_aws_mqtt5_to_mqtt3_adapter_on_listener_detached,
+ .termination_callback_user_data = adapter,
+ };
+ adapter->listener = aws_mqtt5_listener_new(allocator, &listener_config);
+
+ return &adapter->base;
+}
+
+#define DEFAULT_MQTT_ADAPTER_OPERATION_TABLE_SIZE 100
+
+void aws_mqtt5_to_mqtt3_adapter_operation_table_init(
+ struct aws_mqtt5_to_mqtt3_adapter_operation_table *table,
+ struct aws_allocator *allocator) {
+ aws_mutex_init(&table->lock);
+ aws_hash_table_init(
+ &table->operations,
+ allocator,
+ DEFAULT_MQTT_ADAPTER_OPERATION_TABLE_SIZE,
+ aws_mqtt_hash_uint16_t,
+ aws_mqtt_compare_uint16_t_eq,
+ NULL,
+ NULL);
+ table->next_id = 1;
+}
+
+static int s_adapter_operation_fail(void *context, struct aws_hash_element *operation_element) {
+ (void)context;
+
+ struct aws_mqtt5_to_mqtt3_adapter_operation_base *operation = operation_element->value;
+
+ (*operation->vtable->fail_fn)(operation->impl, AWS_ERROR_MQTT_CONNECTION_DESTROYED);
+
+ return AWS_COMMON_HASH_TABLE_ITER_CONTINUE;
+}
+
+static int s_adapter_operation_clean_up(void *context, struct aws_hash_element *operation_element) {
+ (void)context;
+
+ struct aws_mqtt5_to_mqtt3_adapter_operation_base *operation = operation_element->value;
+
+ aws_mqtt5_to_mqtt3_adapter_operation_release(operation);
+
+ return AWS_COMMON_HASH_TABLE_ITER_CONTINUE;
+}
+
+void aws_mqtt5_to_mqtt3_adapter_operation_table_clean_up(struct aws_mqtt5_to_mqtt3_adapter_operation_table *table) {
+ aws_hash_table_foreach(&table->operations, s_adapter_operation_fail, table);
+ aws_hash_table_foreach(&table->operations, s_adapter_operation_clean_up, table);
+
+ aws_hash_table_clean_up(&table->operations);
+
+ aws_mutex_clean_up(&table->lock);
+}
+
+static uint16_t s_next_adapter_id(uint16_t current_id) {
+ if (++current_id == 0) {
+ current_id = 1;
+ }
+
+ return current_id;
+}
+
+int aws_mqtt5_to_mqtt3_adapter_operation_table_add_operation(
+ struct aws_mqtt5_to_mqtt3_adapter_operation_table *table,
+ struct aws_mqtt5_to_mqtt3_adapter_operation_base *operation) {
+
+ operation->id = 0;
+
+ aws_mutex_lock(&table->lock);
+
+ uint16_t current_id = table->next_id;
+ struct aws_hash_element *elem = NULL;
+ for (uint16_t i = 0; i < UINT16_MAX; ++i) {
+ aws_hash_table_find(&table->operations, &current_id, &elem);
+
+ if (elem == NULL) {
+ operation->id = current_id;
+ table->next_id = s_next_adapter_id(current_id);
+
+ if (aws_hash_table_put(&table->operations, &operation->id, operation, NULL)) {
+ operation->id = 0;
+ }
+
+ goto done;
+ }
+
+ current_id = s_next_adapter_id(current_id);
+ }
+
+done:
+
+ aws_mutex_unlock(&table->lock);
+
+ return (operation->id != 0) ? AWS_OP_SUCCESS : aws_raise_error(AWS_ERROR_MQTT_QUEUE_FULL);
+}
+
+void aws_mqtt5_to_mqtt3_adapter_operation_table_remove_operation(
+ struct aws_mqtt5_to_mqtt3_adapter_operation_table *table,
+ uint16_t operation_id) {
+ struct aws_hash_element existing_element;
+ AWS_ZERO_STRUCT(existing_element);
+
+ aws_mutex_lock(&table->lock);
+ aws_hash_table_remove(&table->operations, &operation_id, &existing_element, NULL);
+ aws_mutex_unlock(&table->lock);
+
+ struct aws_mqtt5_to_mqtt3_adapter_operation_base *operation = existing_element.value;
+ if (operation != NULL) {
+ aws_mqtt5_to_mqtt3_adapter_operation_release(operation);
+ }
+}
+
+struct aws_mqtt5_to_mqtt3_adapter_operation_base *aws_mqtt5_to_mqtt3_adapter_operation_release(
+ struct aws_mqtt5_to_mqtt3_adapter_operation_base *operation) {
+ if (operation != NULL) {
+ aws_ref_count_release(&operation->ref_count);
+ }
+
+ return NULL;
+}
+
+struct aws_mqtt5_to_mqtt3_adapter_operation_base *aws_mqtt5_to_mqtt3_adapter_operation_acquire(
+ struct aws_mqtt5_to_mqtt3_adapter_operation_base *operation) {
+ if (operation != NULL) {
+ aws_ref_count_acquire(&operation->ref_count);
+ }
+
+ return operation;
+}
diff --git a/contrib/restricted/aws/aws-c-mqtt/source/v5/mqtt5_topic_alias.c b/contrib/restricted/aws/aws-c-mqtt/source/v5/mqtt5_topic_alias.c
index 928c7719283..4d0185ef11e 100644
--- a/contrib/restricted/aws/aws-c-mqtt/source/v5/mqtt5_topic_alias.c
+++ b/contrib/restricted/aws/aws-c-mqtt/source/v5/mqtt5_topic_alias.c
@@ -7,6 +7,7 @@
#include <aws/common/lru_cache.h>
#include <aws/common/string.h>
+#include <aws/mqtt/private/client_impl_shared.h>
#include <aws/mqtt/private/v5/mqtt5_utils.h>
int aws_mqtt5_inbound_topic_alias_resolver_init(
@@ -131,7 +132,7 @@ static struct aws_mqtt5_outbound_topic_alias_resolver *s_aws_mqtt5_outbound_topi
struct aws_allocator *allocator);
static struct aws_mqtt5_outbound_topic_alias_resolver *s_aws_mqtt5_outbound_topic_alias_resolver_lru_new(
struct aws_allocator *allocator);
-static struct aws_mqtt5_outbound_topic_alias_resolver *s_aws_mqtt5_outbound_topic_alias_resolver_user_new(
+static struct aws_mqtt5_outbound_topic_alias_resolver *s_aws_mqtt5_outbound_topic_alias_resolver_manual_new(
struct aws_allocator *allocator);
struct aws_mqtt5_outbound_topic_alias_resolver *aws_mqtt5_outbound_topic_alias_resolver_new(
@@ -139,8 +140,8 @@ struct aws_mqtt5_outbound_topic_alias_resolver *aws_mqtt5_outbound_topic_alias_r
enum aws_mqtt5_client_outbound_topic_alias_behavior_type outbound_alias_behavior) {
switch (aws_mqtt5_outbound_topic_alias_behavior_type_to_non_default(outbound_alias_behavior)) {
- case AWS_MQTT5_COTABT_USER:
- return s_aws_mqtt5_outbound_topic_alias_resolver_user_new(allocator);
+ case AWS_MQTT5_COTABT_MANUAL:
+ return s_aws_mqtt5_outbound_topic_alias_resolver_manual_new(allocator);
case AWS_MQTT5_COTABT_LRU:
return s_aws_mqtt5_outbound_topic_alias_resolver_lru_new(allocator);
@@ -166,7 +167,7 @@ int aws_mqtt5_outbound_topic_alias_resolver_reset(
uint16_t topic_alias_maximum) {
if (resolver == NULL) {
- return AWS_OP_ERR;
+ return aws_raise_error(AWS_ERROR_INVALID_ARGUMENT);
}
return (*resolver->vtable->reset_fn)(resolver, topic_alias_maximum);
@@ -242,60 +243,60 @@ static struct aws_mqtt5_outbound_topic_alias_resolver *s_aws_mqtt5_outbound_topi
}
/*
- * User resolver
+ * Manual resolver
*
- * User resolution implies the user is controlling the topic alias assignments, but we still want to validate their
+ * Manual resolution implies the user is controlling the topic alias assignments, but we still want to validate their
* actions. In particular, we track the currently valid set of aliases (based on previous outbound publishes)
* and only use an alias when the submitted publish is an exact match for the current assignment.
*/
-struct aws_mqtt5_outbound_topic_alias_resolver_user {
+struct aws_mqtt5_outbound_topic_alias_resolver_manual {
struct aws_mqtt5_outbound_topic_alias_resolver base;
struct aws_array_list aliases;
};
-static void s_cleanup_user_aliases(struct aws_mqtt5_outbound_topic_alias_resolver_user *user_resolver) {
- for (size_t i = 0; i < aws_array_list_length(&user_resolver->aliases); ++i) {
+static void s_cleanup_manual_aliases(struct aws_mqtt5_outbound_topic_alias_resolver_manual *manual_resolver) {
+ for (size_t i = 0; i < aws_array_list_length(&manual_resolver->aliases); ++i) {
struct aws_string *alias = NULL;
- aws_array_list_get_at(&user_resolver->aliases, &alias, i);
+ aws_array_list_get_at(&manual_resolver->aliases, &alias, i);
aws_string_destroy(alias);
}
- aws_array_list_clean_up(&user_resolver->aliases);
- AWS_ZERO_STRUCT(user_resolver->aliases);
+ aws_array_list_clean_up(&manual_resolver->aliases);
+ AWS_ZERO_STRUCT(manual_resolver->aliases);
}
-static void s_aws_mqtt5_outbound_topic_alias_resolver_user_destroy(
+static void s_aws_mqtt5_outbound_topic_alias_resolver_manual_destroy(
struct aws_mqtt5_outbound_topic_alias_resolver *resolver) {
if (resolver == NULL) {
return;
}
- struct aws_mqtt5_outbound_topic_alias_resolver_user *user_resolver = resolver->impl;
- s_cleanup_user_aliases(user_resolver);
+ struct aws_mqtt5_outbound_topic_alias_resolver_manual *manual_resolver = resolver->impl;
+ s_cleanup_manual_aliases(manual_resolver);
- aws_mem_release(resolver->allocator, user_resolver);
+ aws_mem_release(resolver->allocator, manual_resolver);
}
-static int s_aws_mqtt5_outbound_topic_alias_resolver_user_reset(
+static int s_aws_mqtt5_outbound_topic_alias_resolver_manual_reset(
struct aws_mqtt5_outbound_topic_alias_resolver *resolver,
uint16_t topic_alias_maximum) {
- struct aws_mqtt5_outbound_topic_alias_resolver_user *user_resolver = resolver->impl;
- s_cleanup_user_aliases(user_resolver);
+ struct aws_mqtt5_outbound_topic_alias_resolver_manual *manual_resolver = resolver->impl;
+ s_cleanup_manual_aliases(manual_resolver);
aws_array_list_init_dynamic(
- &user_resolver->aliases, resolver->allocator, topic_alias_maximum, sizeof(struct aws_string *));
+ &manual_resolver->aliases, resolver->allocator, topic_alias_maximum, sizeof(struct aws_string *));
for (size_t i = 0; i < topic_alias_maximum; ++i) {
struct aws_string *invalid_alias = NULL;
- aws_array_list_push_back(&user_resolver->aliases, &invalid_alias);
+ aws_array_list_push_back(&manual_resolver->aliases, &invalid_alias);
}
return AWS_OP_SUCCESS;
}
-static int s_aws_mqtt5_outbound_topic_alias_resolver_user_resolve_outbound_publish_fn(
+static int s_aws_mqtt5_outbound_topic_alias_resolver_manual_resolve_outbound_publish_fn(
struct aws_mqtt5_outbound_topic_alias_resolver *resolver,
const struct aws_mqtt5_packet_publish_view *publish_view,
uint16_t *topic_alias_out,
@@ -315,15 +316,15 @@ static int s_aws_mqtt5_outbound_topic_alias_resolver_user_resolve_outbound_publi
return aws_raise_error(AWS_ERROR_MQTT5_INVALID_OUTBOUND_TOPIC_ALIAS);
}
- struct aws_mqtt5_outbound_topic_alias_resolver_user *user_resolver = resolver->impl;
+ struct aws_mqtt5_outbound_topic_alias_resolver_manual *manual_resolver = resolver->impl;
uint16_t user_alias_index = user_alias - 1;
- if (user_alias_index >= aws_array_list_length(&user_resolver->aliases)) {
+ if (user_alias_index >= aws_array_list_length(&manual_resolver->aliases)) {
/* should have been caught by dynamic publish validation */
return aws_raise_error(AWS_ERROR_MQTT5_INVALID_OUTBOUND_TOPIC_ALIAS);
}
struct aws_string *current_assignment = NULL;
- aws_array_list_get_at(&user_resolver->aliases, &current_assignment, user_alias_index);
+ aws_array_list_get_at(&manual_resolver->aliases, &current_assignment, user_alias_index);
*topic_alias_out = user_alias;
@@ -345,25 +346,25 @@ static int s_aws_mqtt5_outbound_topic_alias_resolver_user_resolve_outbound_publi
if (!can_use_alias) {
aws_string_destroy(current_assignment);
current_assignment = aws_string_new_from_cursor(resolver->allocator, &publish_view->topic);
- aws_array_list_set_at(&user_resolver->aliases, &current_assignment, user_alias_index);
+ aws_array_list_set_at(&manual_resolver->aliases, &current_assignment, user_alias_index);
}
return AWS_OP_SUCCESS;
}
-static struct aws_mqtt5_outbound_topic_alias_resolver_vtable s_aws_mqtt5_outbound_topic_alias_resolver_user_vtable = {
- .destroy_fn = s_aws_mqtt5_outbound_topic_alias_resolver_user_destroy,
- .reset_fn = s_aws_mqtt5_outbound_topic_alias_resolver_user_reset,
- .resolve_outbound_publish_fn = s_aws_mqtt5_outbound_topic_alias_resolver_user_resolve_outbound_publish_fn,
+static struct aws_mqtt5_outbound_topic_alias_resolver_vtable s_aws_mqtt5_outbound_topic_alias_resolver_manual_vtable = {
+ .destroy_fn = s_aws_mqtt5_outbound_topic_alias_resolver_manual_destroy,
+ .reset_fn = s_aws_mqtt5_outbound_topic_alias_resolver_manual_reset,
+ .resolve_outbound_publish_fn = s_aws_mqtt5_outbound_topic_alias_resolver_manual_resolve_outbound_publish_fn,
};
-static struct aws_mqtt5_outbound_topic_alias_resolver *s_aws_mqtt5_outbound_topic_alias_resolver_user_new(
+static struct aws_mqtt5_outbound_topic_alias_resolver *s_aws_mqtt5_outbound_topic_alias_resolver_manual_new(
struct aws_allocator *allocator) {
- struct aws_mqtt5_outbound_topic_alias_resolver_user *resolver =
- aws_mem_calloc(allocator, 1, sizeof(struct aws_mqtt5_outbound_topic_alias_resolver_user));
+ struct aws_mqtt5_outbound_topic_alias_resolver_manual *resolver =
+ aws_mem_calloc(allocator, 1, sizeof(struct aws_mqtt5_outbound_topic_alias_resolver_manual));
resolver->base.allocator = allocator;
- resolver->base.vtable = &s_aws_mqtt5_outbound_topic_alias_resolver_user_vtable;
+ resolver->base.vtable = &s_aws_mqtt5_outbound_topic_alias_resolver_manual_vtable;
resolver->base.impl = resolver;
aws_array_list_init_dynamic(&resolver->aliases, allocator, 0, sizeof(struct aws_string *));
@@ -450,13 +451,6 @@ static void s_destroy_assignment_value(void *value) {
s_aws_topic_alias_assignment_destroy(value);
}
-static bool s_topic_hash_equality_fn(const void *a, const void *b) {
- const struct aws_byte_cursor *a_cursor = a;
- const struct aws_byte_cursor *b_cursor = b;
-
- return aws_byte_cursor_eq(a_cursor, b_cursor);
-}
-
static int s_aws_mqtt5_outbound_topic_alias_resolver_lru_reset(
struct aws_mqtt5_outbound_topic_alias_resolver *resolver,
uint16_t topic_alias_maximum) {
@@ -471,7 +465,7 @@ static int s_aws_mqtt5_outbound_topic_alias_resolver_lru_reset(
lru_resolver->lru_cache = aws_cache_new_lru(
lru_resolver->base.allocator,
aws_hash_byte_cursor_ptr,
- s_topic_hash_equality_fn,
+ aws_mqtt_byte_cursor_hash_equality,
NULL,
s_destroy_assignment_value,
topic_alias_maximum);
diff --git a/contrib/restricted/aws/aws-c-mqtt/source/v5/mqtt5_utils.c b/contrib/restricted/aws/aws-c-mqtt/source/v5/mqtt5_utils.c
index 88de757c65f..79dc686bd9d 100644
--- a/contrib/restricted/aws/aws-c-mqtt/source/v5/mqtt5_utils.c
+++ b/contrib/restricted/aws/aws-c-mqtt/source/v5/mqtt5_utils.c
@@ -7,7 +7,6 @@
#include <aws/common/byte_buf.h>
#include <aws/common/device_random.h>
-#include <aws/common/encoding.h>
#include <inttypes.h>
uint8_t aws_mqtt5_compute_fixed_header_byte1(enum aws_mqtt5_packet_type packet_type, uint8_t flags) {
@@ -290,7 +289,7 @@ enum aws_mqtt5_client_session_behavior_type aws_mqtt5_client_session_behavior_ty
const char *aws_mqtt5_outbound_topic_alias_behavior_type_to_c_string(
enum aws_mqtt5_client_outbound_topic_alias_behavior_type outbound_aliasing_behavior) {
switch (aws_mqtt5_outbound_topic_alias_behavior_type_to_non_default(outbound_aliasing_behavior)) {
- case AWS_MQTT5_COTABT_USER:
+ case AWS_MQTT5_COTABT_MANUAL:
return "User-controlled outbound topic aliasing behavior";
case AWS_MQTT5_COTABT_LRU:
return "LRU caching outbound topic aliasing behavior";
@@ -302,6 +301,13 @@ const char *aws_mqtt5_outbound_topic_alias_behavior_type_to_c_string(
}
}
+bool aws_mqtt5_outbound_topic_alias_behavior_type_validate(
+ enum aws_mqtt5_client_outbound_topic_alias_behavior_type outbound_aliasing_behavior) {
+
+ return outbound_aliasing_behavior >= AWS_MQTT5_COTABT_DEFAULT &&
+ outbound_aliasing_behavior <= AWS_MQTT5_COTABT_DISABLED;
+}
+
enum aws_mqtt5_client_outbound_topic_alias_behavior_type aws_mqtt5_outbound_topic_alias_behavior_type_to_non_default(
enum aws_mqtt5_client_outbound_topic_alias_behavior_type outbound_aliasing_behavior) {
if (outbound_aliasing_behavior == AWS_MQTT5_COTABT_DEFAULT) {
@@ -323,6 +329,13 @@ const char *aws_mqtt5_inbound_topic_alias_behavior_type_to_c_string(
}
}
+bool aws_mqtt5_inbound_topic_alias_behavior_type_validate(
+ enum aws_mqtt5_client_inbound_topic_alias_behavior_type inbound_aliasing_behavior) {
+
+ return inbound_aliasing_behavior >= AWS_MQTT5_CITABT_DEFAULT &&
+ inbound_aliasing_behavior <= AWS_MQTT5_CITABT_DISABLED;
+}
+
enum aws_mqtt5_client_inbound_topic_alias_behavior_type aws_mqtt5_inbound_topic_alias_behavior_type_to_non_default(
enum aws_mqtt5_client_inbound_topic_alias_behavior_type inbound_aliasing_behavior) {
if (inbound_aliasing_behavior == AWS_MQTT5_CITABT_DEFAULT) {
@@ -413,7 +426,7 @@ uint64_t aws_mqtt5_client_random_in_range(uint64_t from, uint64_t to) {
static uint8_t s_aws_iot_core_rules_prefix[] = "$aws/rules/";
-struct aws_byte_cursor aws_mqtt5_topic_skip_aws_iot_rules_prefix(struct aws_byte_cursor topic_cursor) {
+static struct aws_byte_cursor s_aws_mqtt5_topic_skip_aws_iot_rules_prefix(struct aws_byte_cursor topic_cursor) {
size_t prefix_length = AWS_ARRAY_SIZE(s_aws_iot_core_rules_prefix) - 1; /* skip 0-terminator */
struct aws_byte_cursor rules_prefix = {
@@ -454,47 +467,18 @@ struct aws_byte_cursor aws_mqtt5_topic_skip_aws_iot_rules_prefix(struct aws_byte
return topic_cursor_copy;
}
-size_t aws_mqtt5_topic_get_segment_count(struct aws_byte_cursor topic_cursor) {
- size_t segment_count = 0;
-
- struct aws_byte_cursor segment_cursor;
- AWS_ZERO_STRUCT(segment_cursor);
-
- while (aws_byte_cursor_next_split(&topic_cursor, '/', &segment_cursor)) {
- ++segment_count;
- }
-
- return segment_count;
-}
-
-bool aws_mqtt_is_valid_topic_filter_for_iot_core(struct aws_byte_cursor topic_filter_cursor) {
- struct aws_byte_cursor post_rule_suffix = aws_mqtt5_topic_skip_aws_iot_rules_prefix(topic_filter_cursor);
- return aws_mqtt5_topic_get_segment_count(post_rule_suffix) <= AWS_IOT_CORE_MAXIMUM_TOPIC_SEGMENTS;
-}
-
-bool aws_mqtt_is_valid_topic_for_iot_core(struct aws_byte_cursor topic_cursor) {
- struct aws_byte_cursor post_rule_suffix = aws_mqtt5_topic_skip_aws_iot_rules_prefix(topic_cursor);
- if (aws_mqtt5_topic_get_segment_count(post_rule_suffix) > AWS_IOT_CORE_MAXIMUM_TOPIC_SEGMENTS) {
- return false;
- }
-
- return post_rule_suffix.len <= AWS_IOT_CORE_MAXIMUM_TOPIC_LENGTH;
-}
-
static uint8_t s_shared_subscription_prefix[] = "$share";
static bool s_is_not_hash_or_plus(uint8_t byte) {
return byte != '+' && byte != '#';
}
-/* $share/{ShareName}/{filter} */
-bool aws_mqtt_is_topic_filter_shared_subscription(struct aws_byte_cursor topic_cursor) {
-
+static struct aws_byte_cursor s_aws_mqtt5_topic_skip_shared_prefix(struct aws_byte_cursor topic_cursor) {
/* shared subscription filters must have an initial segment of "$share" */
struct aws_byte_cursor first_segment_cursor;
AWS_ZERO_STRUCT(first_segment_cursor);
if (!aws_byte_cursor_next_split(&topic_cursor, '/', &first_segment_cursor)) {
- return false;
+ return topic_cursor;
}
struct aws_byte_cursor share_prefix_cursor = {
@@ -503,7 +487,7 @@ bool aws_mqtt_is_topic_filter_shared_subscription(struct aws_byte_cursor topic_c
};
if (!aws_byte_cursor_eq_ignore_case(&share_prefix_cursor, &first_segment_cursor)) {
- return false;
+ return topic_cursor;
}
/*
@@ -512,12 +496,12 @@ bool aws_mqtt_is_topic_filter_shared_subscription(struct aws_byte_cursor topic_c
*/
struct aws_byte_cursor second_segment_cursor = first_segment_cursor;
if (!aws_byte_cursor_next_split(&topic_cursor, '/', &second_segment_cursor)) {
- return false;
+ return topic_cursor;
}
if (second_segment_cursor.len == 0 ||
!aws_byte_cursor_satisfies_pred(&second_segment_cursor, s_is_not_hash_or_plus)) {
- return false;
+ return topic_cursor;
}
/*
@@ -527,48 +511,58 @@ bool aws_mqtt_is_topic_filter_shared_subscription(struct aws_byte_cursor topic_c
size_t remaining_length =
topic_cursor.ptr + topic_cursor.len - (second_segment_cursor.len + second_segment_cursor.ptr);
if (remaining_length == 0) {
- return false;
+ return topic_cursor;
}
aws_byte_cursor_advance(&remaining_cursor, topic_cursor.len - remaining_length + 1);
- if (!aws_mqtt_is_valid_topic_filter(&remaining_cursor)) {
- return false;
- }
+ return remaining_cursor;
+}
- return true;
+struct aws_byte_cursor aws_mqtt5_topic_skip_aws_iot_core_uncounted_prefix(struct aws_byte_cursor topic_cursor) {
+ struct aws_byte_cursor skip_shared = s_aws_mqtt5_topic_skip_shared_prefix(topic_cursor);
+ struct aws_byte_cursor skip_rules = s_aws_mqtt5_topic_skip_aws_iot_rules_prefix(skip_shared);
+
+ return skip_rules;
}
-/* UTF-8 encoded string validation respect to [MQTT-1.5.3-2]. */
-static int aws_mqtt5_utf8_decoder(uint32_t codepoint, void *user_data) {
- (void)user_data;
- /* U+0000 - A UTF-8 Encoded String MUST NOT include an encoding of the null character U+0000. [MQTT-1.5.4-2]
- * U+0001..U+001F control characters are not valid
- */
- if (AWS_UNLIKELY(codepoint <= 0x001F)) {
- return aws_raise_error(AWS_ERROR_MQTT5_INVALID_UTF8_STRING);
- }
+size_t aws_mqtt5_topic_get_segment_count(struct aws_byte_cursor topic_cursor) {
+ size_t segment_count = 0;
- /* U+007F..U+009F control characters are not valid */
- if (AWS_UNLIKELY((codepoint >= 0x007F) && (codepoint <= 0x009F))) {
- return aws_raise_error(AWS_ERROR_MQTT5_INVALID_UTF8_STRING);
- }
+ struct aws_byte_cursor segment_cursor;
+ AWS_ZERO_STRUCT(segment_cursor);
- /* Unicode non-characters are not valid: https://www.unicode.org/faq/private_use.html#nonchar1 */
- if (AWS_UNLIKELY((codepoint & 0x00FFFF) >= 0x00FFFE)) {
- return aws_raise_error(AWS_ERROR_MQTT5_INVALID_UTF8_STRING);
+ while (aws_byte_cursor_next_split(&topic_cursor, '/', &segment_cursor)) {
+ ++segment_count;
}
- if (AWS_UNLIKELY(codepoint >= 0xFDD0 && codepoint <= 0xFDEF)) {
- return aws_raise_error(AWS_ERROR_MQTT5_INVALID_UTF8_STRING);
+
+ return segment_count;
+}
+
+bool aws_mqtt_is_valid_topic_filter_for_iot_core(struct aws_byte_cursor topic_filter_cursor) {
+ struct aws_byte_cursor post_rule_suffix = aws_mqtt5_topic_skip_aws_iot_core_uncounted_prefix(topic_filter_cursor);
+ return aws_mqtt5_topic_get_segment_count(post_rule_suffix) <= AWS_IOT_CORE_MAXIMUM_TOPIC_SEGMENTS;
+}
+
+bool aws_mqtt_is_valid_topic_for_iot_core(struct aws_byte_cursor topic_cursor) {
+ struct aws_byte_cursor post_rule_suffix = aws_mqtt5_topic_skip_aws_iot_core_uncounted_prefix(topic_cursor);
+ if (aws_mqtt5_topic_get_segment_count(post_rule_suffix) > AWS_IOT_CORE_MAXIMUM_TOPIC_SEGMENTS) {
+ return false;
}
- return AWS_ERROR_SUCCESS;
+ return post_rule_suffix.len <= AWS_IOT_CORE_MAXIMUM_TOPIC_LENGTH;
}
-struct aws_utf8_decoder_options g_aws_mqtt5_utf8_decoder_options = {
- .on_codepoint = aws_mqtt5_utf8_decoder,
-};
+/* $share/{ShareName}/{filter} */
+bool aws_mqtt_is_topic_filter_shared_subscription(struct aws_byte_cursor topic_cursor) {
+ struct aws_byte_cursor remaining_cursor = s_aws_mqtt5_topic_skip_shared_prefix(topic_cursor);
+ if (remaining_cursor.len == topic_cursor.len) {
+ return false;
+ }
-int aws_mqtt5_validate_utf8_text(struct aws_byte_cursor text) {
- return aws_decode_utf8(text, &g_aws_mqtt5_utf8_decoder_options);
+ if (!aws_mqtt_is_valid_topic_filter(&remaining_cursor)) {
+ return false;
+ }
+
+ return true;
}
diff --git a/contrib/restricted/aws/aws-c-mqtt/ya.make b/contrib/restricted/aws/aws-c-mqtt/ya.make
index 7dd1d6ba7cf..8f460436aa3 100644
--- a/contrib/restricted/aws/aws-c-mqtt/ya.make
+++ b/contrib/restricted/aws/aws-c-mqtt/ya.make
@@ -6,9 +6,9 @@ LICENSE(Apache-2.0)
LICENSE_TEXTS(.yandex_meta/licenses.list.txt)
-VERSION(0.8.8)
+VERSION(0.10.4)
-ORIGINAL_SOURCE(https://github.com/awslabs/aws-c-mqtt/archive/v0.8.8.tar.gz)
+ORIGINAL_SOURCE(https://github.com/awslabs/aws-c-mqtt/archive/v0.10.4.tar.gz)
PEERDIR(
contrib/restricted/aws/aws-c-common
@@ -31,7 +31,6 @@ CFLAGS(
-DAWS_HTTP_USE_IMPORT_EXPORT
-DAWS_IO_USE_IMPORT_EXPORT
-DAWS_MQTT_USE_IMPORT_EXPORT
- -DAWS_MQTT_WITH_WEBSOCKETS
-DAWS_USE_EPOLL
-DHAVE_SYSCONF
-DINTEL_NO_ITTNOTIFY_API
@@ -46,8 +45,11 @@ ENDIF()
SRCS(
source/client.c
source/client_channel_handler.c
+ source/client_impl_shared.c
source/fixed_header.c
source/mqtt.c
+ source/mqtt311_decoder.c
+ source/mqtt_subscription_set.c
source/packets.c
source/shared_constants.c
source/topic_tree.c
@@ -57,6 +59,7 @@ SRCS(
source/v5/mqtt5_encoder.c
source/v5/mqtt5_listener.c
source/v5/mqtt5_options_storage.c
+ source/v5/mqtt5_to_mqtt3_adapter.c
source/v5/mqtt5_topic_alias.c
source/v5/mqtt5_types.c
source/v5/mqtt5_utils.c