Support multiple subscription identifiers in received messages.

Summary:
- refactor property encoding
- change user property type to be std::pair<std::string, std::string>

Reviewers: ivica

Reviewed By: ivica

Subscribers: korina

Differential Revision: https://repo.mireo.local/D27867
This commit is contained in:
Bruno Iljazovic
2024-02-16 10:36:26 +01:00
parent b40ddb3ced
commit 1acdd99f28
35 changed files with 469 additions and 380 deletions

View File

@ -10,7 +10,7 @@ This section lists all possible __AUTH__ Properties and describes their usage:
[[authentication_method] [`std::string`] [A UTF-8 Encoded String containing the name of the authentication method used for extended authentication.]]
[[authentication_data] [`std::string`] [Binary Data containing authentication data. The contents of the data are defined by the authentication method.]]
[[reason_string] [`std::string`] [A UTF-8 Encoded String representing the reason associated with this response.]]
[[user_property] [`std::vector<std::string>`] [A list of name, value pairs (__UTF8_STRING_PAIR__) defining User Properties.
[[user_property] [`std::pair<std::string, std::string>`] [Name, value pair (__UTF8_STRING_PAIR__) defining User Property. There can be multiple pairs in one packet.
This property may be used to provide additional diagnostic or other information. ]]
]
@ -19,6 +19,9 @@ After obtaining an instance of `async_mqtt5::auth_props`, the subscript operator
The Identifiers listed in the table above are available within the `async_mqtt5::prop` namespace for Property access.
[note When accessing a property value, the subscript operator will return a `std::optional` of the value type for all properties,
except for `async_mqtt5::prop::user_property`, where it will return an instance of `std::vector<std::pair<std::string, std::string>>`.]
[h4 Example]
The following example shows how to set a Property value:
@ -27,17 +30,21 @@ The following example shows how to set a Property value:
async_mqtt5::auth_props props;
props[async_mqtt5::prop::authentication_method] = "SCRAM-SHA-1";
props[async_mqtt5::prop::authentication_data] = "data";
props[async_mqtt5::prop::user_property].emplace_back("name", "value");
The following example shows how to retrieve a Property value:
[note When retrieving a property value, the subscript operator will return a `std::optional` of the value type for all properties,
except for `async_mqtt5::prop::user_property`, where it will return an instance of its value type, `std::vector<std::string>`.]
[!c++]
std::optional<std::string> auth_data = props[async_mqtt5::prop::authentication_data];
if (auth_data.has_value())
// authentication data property was previously set
else
// authentication data property was not set
std::vector<std::pair<std::string, std::string>>& user_props = props[async_mqtt5::prop::user_property];
if (!user_props.empty())
// user property was previously set
else
// user property was not set
[endsect]

View File

@ -15,7 +15,7 @@ This section lists all possible __CONNACK__ Properties and describes their usage
[[assigned_client_identifier] [`std::string`] [The Client Identifier which was assigned by the Server because a zero length Client Identifier was found in the __CONNECT__ packet]]
[[topic_alias_maximum] [`uint16_t`] [The highest value that the Server will accept as a Topic Alias sent by the Client.]]
[[reason_string] [`std::string`] [A UTF-8 Encoded String representing the reason associated with this response.]]
[[user_property] [`std::vector<std::string>`] [A list of name, value pairs (__UTF8_STRING_PAIR__) defining User Properties.
[[user_property] [`std::pair<std::string, std::string>`] [Name, value pair (__UTF8_STRING_PAIR__) defining User Property. There can be multiple pairs in one packet.
The meaning of these properties is not defined by the specification.]]
[[wildcard_subscription_available] [`uint8_t`] [A value of 0 means that Wildcard Subscriptions are not supported.
A value of 1 means they are supported. If not present, they are supported.]]
@ -35,6 +35,9 @@ After obtaining an instance of `async_mqtt5::connack_props`, the subscript opera
The Identifiers listed in the table above are available within the `async_mqtt5::prop` namespace for Property access.
[note When accessing a property value, the subscript operator will return a `std::optional` of the value type for all properties,
except for `async_mqtt5::prop::user_property`, where it will return an instance of `std::vector<std::pair<std::string, std::string>>`.]
[h4 Example]
The following example shows how to set a Property value:
@ -43,17 +46,21 @@ The following example shows how to set a Property value:
async_mqtt5::connack_props props;
props[async_mqtt5::prop::maximum_packet_size] = 65535;
props[async_mqtt5::prop::assigned_client_identifier] = "ClientID";
props[async_mqtt5::prop::user_property].emplace_back("name", "value");
The following example shows how to retrieve a Property value:
[note When retrieving a property value, the subscript operator will return a `std::optional` of the value type for all properties,
except for `async_mqtt5::prop::user_property`, where it will return an instance of its value type, `std::vector<std::string>`.]
[!c++]
std::optional<std::string> auth_method = props[async_mqtt5::prop::authentication_method];
if (auth_method.has_value())
// authentication method property was previously set
else
// authentication method property was not set
std::vector<std::pair<std::string, std::string>>& user_props = props[async_mqtt5::prop::user_property];
if (!user_props.empty())
// user property was previously set
else
// user property was not set
[endsect]

View File

@ -15,7 +15,7 @@ This section lists all possible __CONNECT__ Properties and describes their usage
[[request_problem_information] [`uint8_t`] [The value of 0 signals that the Server MAY return a Reason String or User Properties on a __CONNACK__ or __DISCONNECT__ packet,
but MUST NOT send them on any packet other than __PUBLISH__, __CONNACK__, or __DISCONNECT__.
If the value is 1, the Server MAY return a Reason String or User Properties where it is allowed.]]
[[user_property] [`std::vector<std::string>`] [A list of name, value pairs (__UTF8_STRING_PAIR__) defining User Properties.
[[user_property] [`std::pair<std::string, std::string>`] [Name, value pair (__UTF8_STRING_PAIR__) defining User Property. There can be multiple pairs in one packet.
The meaning of these properties is not defined by the specification.]]
[[authentication_method] [`std::string`] [A UTF-8 Encoded String containing the name of the authentication method used for extended authentication.]]
[[authentication_data] [`std::string`] [Binary Data containing authentication data. The contents of the data are defined by the authentication method.]]
@ -26,6 +26,9 @@ After obtaining an instance of `async_mqtt5::connect_props`, the subscript opera
The Identifiers listed in the table above are available within the `async_mqtt5::prop` namespace for Property access.
[note When accessing a property value, the subscript operator will return a `std::optional` of the value type for all properties,
except for `async_mqtt5::prop::user_property`, where it will return an instance of `std::vector<std::pair<std::string, std::string>>`.]
[h4 Example]
The following example shows how to set a Property value:
@ -34,17 +37,21 @@ The following example shows how to set a Property value:
async_mqtt5::connect_props props;
props[async_mqtt5::prop::session_expiry_interval] = 1200;
props[async_mqtt5::prop::receive_maximum] = uint16_t(100);
props[async_mqtt5::prop::user_property].emplace_back("name", "value");
The following example shows how to retrieve a Property value:
[note When retrieving a property value, the subscript operator will return a `std::optional` of the value type for all properties,
except for `async_mqtt5::prop::user_property`, where it will return an instance of its value type, `std::vector<std::string>`.]
[!c++]
std::optional<std::string> auth_method = props[async_mqtt5::prop::authentication_method];
if (auth_method.has_value())
// authentication method property was previously set
else
// authentication method property was not set
std::vector<std::pair<std::string, std::string>>& user_props = props[async_mqtt5::prop::user_property];
if (!user_props.empty())
// user property was previously set
else
// user property was not set
[endsect]

View File

@ -9,7 +9,7 @@ This section lists all possible __DISCONNECT__ Properties and describes their us
[[Identifier] [Value type] [Description]]
[[session_expiry_interval] [`uint32_t`] [Represents the Session Expiry Internal in seconds. Can only be sent by the Client.]]
[[reason_string] [`std::string`] [A UTF-8 Encoded String representing the reason associated with this response.]]
[[user_property] [`std::vector<std::string>`] [A list of name, value pairs (__UTF8_STRING_PAIR__) defining User Properties.
[[user_property] [`std::pair<std::string, std::string>`] [Name, value pair (__UTF8_STRING_PAIR__) defining User Property. There can be multiple pairs in one packet.
This property may be used to provide additional diagnostic or other information. ]]
[[server_reference] [`std::string`] [A UTF-8 Encoded String which can be used by the Client to identfy another Server to use.]]
]
@ -19,6 +19,9 @@ After obtaining an instance of `async_mqtt5::disconnect_props`, the subscript op
The Identifiers listed in the table above are available within the `async_mqtt5::prop` namespace for Property access.
[note When accessing a property value, the subscript operator will return a `std::optional` of the value type for all properties,
except for `async_mqtt5::prop::user_property`, where it will return an instance of `std::vector<std::pair<std::string, std::string>>`.]
[h4 Example]
The following example shows how to set a Property value:
@ -26,17 +29,21 @@ The following example shows how to set a Property value:
[!c++]
async_mqtt5::disconnect_props props;
props[async_mqtt5::prop::reason_string] = "Lost connection!";
props[async_mqtt5::prop::user_property].emplace_back("name", "value");
The following example shows how to retrieve a Property value:
[note When retrieving a property value, the subscript operator will return a `std::optional` of the value type for all properties,
except for `async_mqtt5::prop::user_property`, where it will return an instance of its value type, `std::vector<std::string>`.]
[!c++]
std::optional<std::string> reason_string = props[async_mqtt5::prop::reason_string];
if (reason_string.has_value())
// reason string property was previously set
else
// reason string property was not set
std::vector<std::pair<std::string, std::string>>& user_props = props[async_mqtt5::prop::user_property];
if (!user_props.empty())
// user property was previously set
else
// user property was not set
[endsect]

View File

@ -8,7 +8,7 @@ This section lists all possible __PUBACK__ Properties and describes their usage:
[table:puback_props PUBACK properties
[[Identifier] [Value type] [Description]]
[[reason_string] [`std::string`] [A UTF-8 Encoded String representing the reason associated with this response.]]
[[user_property] [`std::vector<std::string>`] [A list of name, value pairs (__UTF8_STRING_PAIR__) defining User Properties.
[[user_property] [`std::pair<std::string, std::string>`] [Name, value pair (__UTF8_STRING_PAIR__) defining User Property. There can be multiple pairs in one packet.
This property may be used to provide additional diagnostic or other information. ]]
]
@ -17,6 +17,9 @@ After obtaining an instance of `async_mqtt5::puback_props`, the subscript operat
The Identifiers listed in the table above are available within the `async_mqtt5::prop` namespace for Property access.
[note When accessing a property value, the subscript operator will return a `std::optional` of the value type for all properties,
except for `async_mqtt5::prop::user_property`, where it will return an instance of `std::vector<std::pair<std::string, std::string>>`.]
[h4 Example]
The following example shows how to set a Property value:
@ -24,17 +27,21 @@ The following example shows how to set a Property value:
[!c++]
async_mqtt5::puback_props props;
props[async_mqtt5::prop::reason_string] = "Some reason...";
props[async_mqtt5::prop::user_property].emplace_back("name", "value");
The following example shows how to retrieve a Property value:
[note When retrieving a property value, the subscript operator will return a `std::optional` of the value type for all properties,
except for `async_mqtt5::prop::user_property`, where it will return an instance of its value type, `std::vector<std::string>`.]
[!c++]
std::optional<std::string> reason_string = props[async_mqtt5::prop::reason_string];
if (reason_string.has_value())
// reason string property was previously set
else
// reason string property was not set
std::vector<std::pair<std::string, std::string>>& user_props = props[async_mqtt5::prop::user_property];
if (!user_props.empty())
// user property was previously set
else
// user property was not set
[endsect]

View File

@ -8,7 +8,7 @@ This section lists all possible __PUBCOMP__ Properties and describes their usage
[table:pubcomp_props PUBCOMP properties
[[Identifier] [Value type] [Description]]
[[reason_string] [`std::string`] [A UTF-8 Encoded String representing the reason associated with this response.]]
[[user_property] [`std::vector<std::string>`] [A list of name, value pairs (__UTF8_STRING_PAIR__) defining User Properties.
[[user_property] [`std::pair<std::string, std::string>`] [Name, value pair (__UTF8_STRING_PAIR__) defining User Property. There can be multiple pairs in one packet.
This property may be used to provide additional diagnostic or other information. ]]
]
@ -17,6 +17,9 @@ After obtaining an instance of `async_mqtt5::pubcomp_props`, the subscript opera
The Identifiers listed in the table above are available within the `async_mqtt5::prop` namespace for Property access.
[note When accessing a property value, the subscript operator will return a `std::optional` of the value type for all properties,
except for `async_mqtt5::prop::user_property`, where it will return an instance of `std::vector<std::pair<std::string, std::string>>`.]
[h4 Example]
The following example shows how to set a Property value:
@ -24,17 +27,21 @@ The following example shows how to set a Property value:
[!c++]
async_mqtt5::pubcomp_props props;
props[async_mqtt5::prop::reason_string] = "Some reason...";
props[async_mqtt5::prop::user_property].emplace_back("name", "value");
The following example shows how to retrieve a Property value:
[note When retrieving a property value, the subscript operator will return a `std::optional` of the value type for all properties,
except for `async_mqtt5::prop::user_property`, where it will return an instance of its value type, `std::vector<std::string>`.]
[!c++]
std::optional<std::string> reason_string = props[async_mqtt5::prop::reason_string];
if (reason_string.has_value())
// reason string property was previously set
else
// reason string property was not set
std::vector<std::pair<std::string, std::string>>& user_props = props[async_mqtt5::prop::user_property];
if (!user_props.empty())
// user property was previously set
else
// user property was not set
[endsect]

View File

@ -12,9 +12,9 @@ This section lists all possible __PUBLISH__ Properties and describes their usage
[[topic_alias] [`uint16_t`] [Two Byte integer representing the Topic Alias, an integer value that is used to identify the Topic instead of using the Topic Name.]]
[[response_topic] [`std::string`] [A UTF-8 Encoded String which is used as the Topic Name for a response message.]]
[[correlation_data] [`std::string`] [Binary Data used by the sender of the Request Message to identify which request the Response Message is for when it is received.]]
[[user_property] [`std::vector<std::string>`] [A list of name, value pairs (__UTF8_STRING_PAIR__) defining User Properties.
[[user_property] [`std::pair<std::string, std::string>`] [Name, value pair (__UTF8_STRING_PAIR__) defining User Property. There can be multiple pairs in one packet.
The meaning of these properties is not defined by the specification.]]
[[subscription_identifier] [`int32_t`] [Identifier of the Subscription in range of 1 to 268,435,455.]]
[[subscription_identifier] [`int32_t`] [Identifier of the matching subscription. If there are multiple matching subscriptions, multiple identifiers may be included.]]
[[content_type] [`std::string`] [A UTF-8 Encoded String describing the content of the Application Message.]]
]
@ -23,6 +23,11 @@ After obtaining an instance of `async_mqtt5::publish_props`, the subscript opera
The Identifiers listed in the table above are available within the `async_mqtt5::prop` namespace for Property access.
[note When accessing a property value, the subscript operator will return a `std::optional` of the value type for all properties,
except for `async_mqtt5::prop::user_property` and `async_mqtt5::prop::subscription_identifier`, where it will return an instance of
`std::vector<std::pair<std::string, std::string>>` and `async_mqtt5::prop::subscription_identifiers` respectively.
`async_mqtt5::prop::subscription_identifiers` has the interface of `boost::container::small_vector`.]
[h4 Example]
The following example shows how to set a Property value:
@ -32,17 +37,21 @@ The following example shows how to set a Property value:
props[async_mqtt5::prop::payload_format_indicator] = uint8_t(1);
props[async_mqtt5::prop::topic_alias] = uint16_t(12);
props[async_mqtt5::prop::response_topic] = "response_topic";
props[async_mqtt5::prop::subscription_identifier].push_back(40);
The following example shows how to retrieve a Property value:
[note When retrieving a property value, the subscript operator will return a `std::optional` of the value type for all properties,
except for `async_mqtt5::prop::user_property`, where it will return an instance of its value type, `std::vector<std::string>`.]
[!c++]
std::optional<uint16_t> topic_alias = props[async_mqtt5::prop::topic_alias];
if (topic_alias.has_value())
// topic alias property was previously set
else
// topic alias property was not set
async_mqtt5::prop::subscription_identifiers& sub_ids = props[async_mqtt5::prop::subscription_identifier];
if (!sub_ids.empty())
// subscription identifier property was previously set
else
// subscription identifier property was not set
[endsect]

View File

@ -8,7 +8,7 @@ This section lists all possible __PUBREC__ Properties and describes their usage:
[table:pubrec_props PUBREC properties
[[Identifier] [Value type] [Description]]
[[reason_string] [`std::string`] [A UTF-8 Encoded String representing the reason associated with this response.]]
[[user_property] [`std::vector<std::string>`] [A list of name, value pairs (__UTF8_STRING_PAIR__) defining User Properties.
[[user_property] [`std::pair<std::string, std::string>`] [Name, value pair (__UTF8_STRING_PAIR__) defining User Property. There can be multiple pairs in one packet.
This property may be used to provide additional diagnostic or other information. ]]
]
@ -17,6 +17,9 @@ After obtaining an instance of `async_mqtt5::pubrec_props`, the subscript operat
The Identifiers listed in the table above are available within the `async_mqtt5::prop` namespace for Property access.
[note When accessing a property value, the subscript operator will return a `std::optional` of the value type for all properties,
except for `async_mqtt5::prop::user_property`, where it will return an instance of `std::vector<std::pair<std::string, std::string>>`.]
[h4 Example]
The following example shows how to set a Property value:
@ -24,17 +27,21 @@ The following example shows how to set a Property value:
[!c++]
async_mqtt5::pubrec_props props;
props[async_mqtt5::prop::reason_string] = "Some reason...";
props[async_mqtt5::prop::user_property].emplace_back("name", "value");
The following example shows how to retrieve a Property value:
[note When retrieving a property value, the subscript operator will return a `std::optional` of the value type for all properties,
except for `async_mqtt5::prop::user_property`, where it will return an instance of its value type, `std::vector<std::string>`.]
[!c++]
std::optional<std::string> reason_string = props[async_mqtt5::prop::reason_string];
if (reason_string.has_value())
// reason string property was previously set
else
// reason string property was not set
std::vector<std::pair<std::string, std::string>>& user_props = props[async_mqtt5::prop::user_property];
if (!user_props.empty())
// user property was previously set
else
// user property was not set
[endsect]

View File

@ -8,7 +8,7 @@ This section lists all possible __PUBREL__ Properties and describes their usage:
[table:pubrel_props PUBREL properties
[[Identifier] [Value type] [Description]]
[[reason_string] [`std::string`] [A UTF-8 Encoded String representing the reason associated with this response.]]
[[user_property] [`std::vector<std::string>`] [A list of name, value pairs (__UTF8_STRING_PAIR__) defining User Properties.
[[user_property] [`std::pair<std::string, std::string>`] [Name, value pair (__UTF8_STRING_PAIR__) defining User Property. There can be multiple pairs in one packet.
This property may be used to provide additional diagnostic or other information. ]]
]
@ -17,23 +17,30 @@ After obtaining an instance of `async_mqtt5::pubrel_props`, the subscript operat
The Identifiers listed in the table above are available within the `async_mqtt5::prop` namespace for Property access.
[note When accessing a property value, the subscript operator will return a `std::optional` of the value type for all properties,
except for `async_mqtt5::prop::user_property`, where it will return an instance of `std::vector<std::pair<std::string, std::string>>`.]
[h4 Example]
The following example shows how to set a Property value:
[!c++]
async_mqtt5::pubrel_props props;
props[async_mqtt5::prop::reason_string] = "Some reason...";
props[async_mqtt5::prop::user_property].emplace_back("name", "value");
The following example shows how to retrieve a Property value:
[note When retrieving a property value, the subscript operator will return a `std::optional` of the value type for all properties,
except for `async_mqtt5::prop::user_property`, where it will return an instance of its value type, `std::vector<std::string>`.]
[!c++]
std::optional<std::string> reason_string = props[async_mqtt5::prop::reason_string];
if (reason_string.has_value())
// reason string property was previously set
else
// reason string property was not set
std::vector<std::pair<std::string, std::string>>& user_props = props[async_mqtt5::prop::user_property];
if (!user_props.empty())
// user property was previously set
else
// user property was not set
[endsect]

View File

@ -8,7 +8,7 @@ This section lists all possible __SUBACK__ Properties and describes their usage:
[table:suback_props SUBACK properties
[[Identifier] [Value type] [Description]]
[[reason_string] [`std::string`] [A UTF-8 Encoded String representing the reason associated with this response.]]
[[user_property] [`std::vector<std::string>`] [A list of name, value pairs (__UTF8_STRING_PAIR__) defining User Properties.
[[user_property] [`std::pair<std::string, std::string>`] [Name, value pair (__UTF8_STRING_PAIR__) defining User Property. There can be multiple pairs in one packet.
This property may be used to provide additional diagnostic or other information. ]]
]
@ -17,6 +17,9 @@ After obtaining an instance of `async_mqtt5::suback_props`, the subscript operat
The Identifiers listed in the table above are available within the `async_mqtt5::prop` namespace for Property access.
[note When accessing a property value, the subscript operator will return a `std::optional` of the value type for all properties,
except for `async_mqtt5::prop::user_property`, where it will return an instance of `std::vector<std::pair<std::string, std::string>>`.]
[h4 Example]
The following example shows how to set a Property value:
@ -24,17 +27,21 @@ The following example shows how to set a Property value:
[!c++]
async_mqtt5::suback_props props;
props[async_mqtt5::prop::reason_string] = "Some reason...";
props[async_mqtt5::prop::user_property].emplace_back("name", "value");
The following example shows how to retrieve a Property value:
[note When retrieving a property value, the subscript operator will return a `std::optional` of the value type for all properties,
except for `async_mqtt5::prop::user_property`, where it will return an instance of its value type, `std::vector<std::string>`.]
[!c++]
std::optional<std::string> reason_string = props[async_mqtt5::prop::reason_string];
if (reason_string.has_value())
// reason string property was previously set
else
// reason string property was not set
std::vector<std::pair<std::string, std::string>>& user_props = props[async_mqtt5::prop::user_property];
if (!user_props.empty())
// user property was previously set
else
// user property was not set
[endsect]

View File

@ -8,7 +8,7 @@ This section lists all possible __SUBSCRIBE__ Properties and describes their usa
[table:subscribe_props SUBSCRIBE properties
[[Identifier] [Value type] [Description]]
[[subscription_identifier] [`int32_t`] [Identifier of the Subscription in range of 1 to 268,435,455.]]
[[user_property] [`std::vector<std::string>`] [A list of name, value pairs (__UTF8_STRING_PAIR__) defining User Properties.
[[user_property] [`std::pair<std::string, std::string>`] [Name, value pair (__UTF8_STRING_PAIR__) defining User Property. There can be multiple pairs in one packet.
This property can be used to send subscription related properties from the Client to the Server.
The meaning of these properties is not defined by the specification ]]
]
@ -18,6 +18,11 @@ After obtaining an instance of `async_mqtt5::subscribe_props`, the subscript ope
The Identifiers listed in the table above are available within the `async_mqtt5::prop` namespace for Property access.
[note When accessing a property value, the subscript operator will return a `std::optional` of the value type for all properties,
except for `async_mqtt5::prop::user_property` and `async_mqtt5::prop::subscription_identifier`, where it will return an instance of
`std::vector<std::pair<std::string, std::string>>` and `async_mqtt5::prop::subscription_identifiers` respectively.
`async_mqtt5::prop::subscription_identifiers` has the interface of `std::optional<int32_t>`.]
[h4 Example]
The following example shows how to set a Property value:
@ -25,17 +30,21 @@ The following example shows how to set a Property value:
[!c++]
async_mqtt5::subscribe_props props;
props[async_mqtt5::prop::subscription_identifier] = 1234;
props[async_mqtt5::prop::user_property].emplace_back("name", "value");
The following example shows how to retrieve a Property value:
[note When retrieving a property value, the subscript operator will return a `std::optional` of the value type for all properties,
except for `async_mqtt5::prop::user_property`, where it will return an instance of its value type, `std::vector<std::string>`.]
[!c++]
std::optional<int32_t> sub_id = props[async_mqtt5::prop::subscription_identifier];
async_mqtt5::prop::subscription_identifiers sub_id = props[async_mqtt5::prop::subscription_identifier];
if (sub_id.has_value())
// subscription identifier property was previously set
else
// subscription identifier property was not set
std::vector<std::pair<std::string, std::string>>& user_props = props[async_mqtt5::prop::user_property];
if (!user_props.empty())
// user property was previously set
else
// user property was not set
[endsect]

View File

@ -8,7 +8,7 @@ This section lists all possible __UNSUBACK__ Properties and describes their usag
[table:unsuback_props UNSUBACK properties
[[Identifier] [Value type] [Description]]
[[reason_string] [`std::string`] [A UTF-8 Encoded String representing the reason associated with this response.]]
[[user_property] [`std::vector<std::string>`] [A list of name, value pairs (__UTF8_STRING_PAIR__) defining User Properties.
[[user_property] [`std::pair<std::string, std::string>`] [Name, value pair (__UTF8_STRING_PAIR__) defining User Property. There can be multiple pairs in one packet.
This property may be used to provide additional diagnostic or other information. ]]
]
@ -17,6 +17,9 @@ After obtaining an instance of `async_mqtt5::unsuback_props`, the subscript oper
The Identifiers listed in the table above are available within the `async_mqtt5::prop` namespace for Property access.
[note When accessing a property value, the subscript operator will return a `std::optional` of the value type for all properties,
except for `async_mqtt5::prop::user_property`, where it will return an instance of `std::vector<std::pair<std::string, std::string>>`.]
[h4 Example]
The following example shows how to set a Property value:
@ -24,17 +27,21 @@ The following example shows how to set a Property value:
[!c++]
async_mqtt5::unsuback_props props;
props[async_mqtt5::prop::reason_string] = "Some reason...";
props[async_mqtt5::prop::user_property].emplace_back("name", "value");
The following example shows how to retrieve a Property value:
[note When retrieving a property value, the subscript operator will return a `std::optional` of the value type for all properties,
except for `async_mqtt5::prop::user_property`, where it will return an instance of its value type, `std::vector<std::string>`.]
[!c++]
std::optional<std::string> reason_string = props[async_mqtt5::prop::reason_string];
if (reason_string.has_value())
// reason string property was previously set
else
// reason string property was not set
std::vector<std::pair<std::string, std::string>>& user_props = props[async_mqtt5::prop::user_property];
if (!user_props.empty())
// user property was previously set
else
// user property was not set
[endsect]

View File

@ -7,7 +7,7 @@ This section lists all possible __UNSUBSCRIBE__ Properties and describes their u
[table:unsubscribe_props UNSUBSCRIBE properties
[[Identifier] [Value type] [Description]]
[[user_property] [`std::vector<std::string>`] [A list of name, value pairs (__UTF8_STRING_PAIR__) defining User Properties.
[[user_property] [`std::pair<std::string, std::string>`] [Name, value pair (__UTF8_STRING_PAIR__) defining User Property. There can be multiple pairs in one packet.
This property can be used to send subscription related properties from the Client to the Server.
The meaning of these properties is not defined by the specification ]]
]
@ -17,22 +17,25 @@ After obtaining an instance of `async_mqtt5::unsubscribe_props`, the subscript o
The Identifiers listed in the table above are available within the `async_mqtt5::prop` namespace for Property access.
[note When accessing a property value, the subscript operator will return a `std::optional` of the value type for all properties,
except for `async_mqtt5::prop::user_property`, where it will return an instance of `std::vector<std::pair<std::string, std::string>>`.]
[h4 Example]
The following example shows how to set a Property value:
[!c++]
async_mqtt5::unsubscribe_props props;
props[async_mqtt5::prop::user_property].push_back("key");
props[async_mqtt5::prop::user_property].push_back("value");
props[async_mqtt5::prop::user_property].emplace_back("name", "value");
The following example shows how to retrieve a Property value:
[note When retrieving a property value, the subscript operator will return a `std::optional` of the value type for all properties,
except for `async_mqtt5::prop::user_property`, where it will return an instance of its value type, `std::vector<std::string>`.]
[!c++]
std::vector<std::string> user_props = props[async_mqtt5::prop::user_property];
std::vector<std::pair<std::string, std::string>>& user_props = props[async_mqtt5::prop::user_property];
if (!user_props.empty())
// user property was previously set
else
// user property was not set
[endsect]

View File

@ -14,7 +14,7 @@ This section lists all possible [reflink2 will Will] Properties and describes th
[[content_type] [`std::string`] [A UTF-8 Encoded String describing the content of the Will Message.]]
[[response_topic] [`std::string`] [A UTF-8 Encoded String which is used as the Topic Name for a response message.]]
[[correlation_data] [`std::string`] [Binary Data used by the sender of the Request Message to identify which request the Response Message is for when it is received.]]
[[user_property] [`std::vector<std::string>`] [A list of name, value pairs (__UTF8_STRING_PAIR__) defining User Properties.
[[user_property] [`std::pair<std::string, std::string>`] [Name, value pair (__UTF8_STRING_PAIR__) defining User Property. There can be multiple pairs in one packet.
The meaning of these properties is not defined by the specification.]]
]
@ -23,6 +23,9 @@ After creating an instance of [reflink2 will `async_mqtt5::will`], the subscript
The Identifiers listed in the table above are available within the `async_mqtt5::prop` namespace for Property access.
[note When accessing a property value, the subscript operator will return a `std::optional` of the value type for all properties,
except for `async_mqtt5::prop::user_property`, where it will return an instance of `std::vector<std::pair<std::string, std::string>>`.]
[h4 Example]
The following example shows how to set a Property value:
@ -31,12 +34,10 @@ The following example shows how to set a Property value:
async_mqtt5::will will;
will[async_mqtt5::prop::message_expiry_interval] = 90;
will[async_mqtt5::prop::content_type] = "Notification";
props[async_mqtt5::prop::user_property].emplace_back("name", "value");
The following example shows how to retrieve a Property value:
[note When retrieving a property value, the subscript operator will return a `std::optional` of the value type for all properties,
except for `async_mqtt5::prop::user_property`, where it will return an instance of its value type, `std::vector<std::string>`.]
[!c++]
std::optional<std::string> c_type = will[async_mqtt5::prop::content_type];
if (c_type.has_value())
@ -44,4 +45,10 @@ except for `async_mqtt5::prop::user_property`, where it will return an instance
else
// content type property was not set
std::vector<std::pair<std::string, std::string>>& user_props = props[async_mqtt5::prop::user_property];
if (!user_props.empty())
// user property was previously set
else
// user property was not set
[endsect]

View File

@ -94,6 +94,13 @@ inline validation_result validate_mqtt_utf8(std::string_view str) {
return validate_impl(str, is_valid_string_size, is_utf8);
}
inline bool is_valid_string_pair(
const std::pair<std::string, std::string>& str_pair
) {
return validate_mqtt_utf8(str_pair.first) == validation_result::valid &&
validate_mqtt_utf8(str_pair.second) == validation_result::valid;
}
} // namespace async_mqtt5::detail
#endif //ASYNC_MQTT5_UTF8_MQTT_HPP

View File

@ -349,49 +349,38 @@ bool parse_to_prop(
It& iter, const It last,
const Ctx& ctx, RCtx& rctx, Prop& prop
) {
using prop_type = decltype(prop);
using prop_type = std::remove_reference_t<decltype(prop)>;
bool rv = false;
if constexpr (is_optional<prop_type>) {
using value_type =
typename std::remove_reference_t<prop_type>::value_type;
if constexpr (std::is_same_v<value_type, uint8_t>) {
uint8_t attr;
rv = x3::byte_.parse(iter, last, ctx, rctx, attr);
prop = attr;
}
if constexpr (std::is_same_v<value_type, uint16_t>) {
uint16_t attr;
rv = x3::big_word.parse(iter, last, ctx, rctx, attr);
prop = attr;
}
if constexpr (std::is_same_v<value_type, int32_t>) {
int32_t attr;
rv = basic::varint_.parse(iter, last, ctx, rctx, attr);
prop = attr;
}
if constexpr (std::is_same_v<value_type, uint32_t>) {
uint32_t attr;
rv = x3::big_dword.parse(iter, last, ctx, rctx, attr);
prop = attr;
}
if constexpr (std::is_same_v<value_type, std::string>) {
std::string attr;
rv = basic::utf8_.parse(iter, last, ctx, rctx, attr);
prop.emplace(std::move(attr));
}
if constexpr (std::is_same_v<prop_type, uint8_t>)
rv = x3::byte_.parse(iter, last, ctx, rctx, prop);
else if constexpr (std::is_same_v<prop_type, uint16_t>)
rv = x3::big_word.parse(iter, last, ctx, rctx, prop);
else if constexpr (std::is_same_v<prop_type, int32_t>)
rv = basic::varint_.parse(iter, last, ctx, rctx, prop);
else if constexpr (std::is_same_v<prop_type, uint32_t>)
rv = x3::big_dword.parse(iter, last, ctx, rctx, prop);
else if constexpr (std::is_same_v<prop_type, std::string>)
rv = basic::utf8_.parse(iter, last, ctx, rctx, prop);
else if constexpr (is_optional<prop_type>) {
typename prop_type::value_type val;
rv = parse_to_prop(iter, last, ctx, rctx, val);
if (rv) prop.emplace(std::move(val));
}
if constexpr (async_mqtt5::is_vector<prop_type>) {
std::string value;
// key
rv = basic::utf8_.parse(iter, last, ctx, rctx, value);
if (rv) prop.push_back(std::move(value));
// value
rv = basic::utf8_.parse(iter, last, ctx, rctx, value);
else if constexpr (is_pair<prop_type>) {
rv = parse_to_prop(iter, last, ctx, rctx, prop.first);
rv = parse_to_prop(iter, last, ctx, rctx, prop.second);
}
else if constexpr (is_vector<prop_type> || is_small_vector<prop_type>) {
typename std::remove_reference_t<prop_type>::value_type value;
rv = parse_to_prop(iter, last, ctx, rctx, value);
if (rv) prop.push_back(std::move(value));
}
return rv;
}

View File

@ -320,84 +320,26 @@ std::string& operator<<(std::string& s, T&& t) {
} // end namespace basic
namespace detail {
namespace pp = async_mqtt5::prop;
template <pp::property_type p, std::size_t I, typename Tuple>
constexpr bool match_v = std::is_same_v<
std::integral_constant<pp::property_type, p>,
typename std::tuple_element_t<I, Tuple>::key
>;
template <
pp::property_type p, typename Tuple,
typename Idxs = std::make_index_sequence<std::tuple_size_v<Tuple>>
>
struct type_index;
template <
pp::property_type p, template <typename...> typename Tuple,
typename... Args, std::size_t... Is
>
struct type_index<p, Tuple<Args...>, std::index_sequence<Is...>> :
std::integral_constant<
std::size_t, ((Is * match_v<p, Is, Tuple<Args...>>)+... + 0)
>
{
static_assert(
1 == (match_v<p, Is, Tuple<Args...>> + ... + 0),
"T doesn't appear once in tuple"
);
};
} // end namespace detail
namespace prop {
namespace pp = async_mqtt5::prop;
template <pp::property_type p, typename T>
struct prop_encoder_type {
using key = std::integral_constant<pp::property_type, p>;
using value = T;
};
using encoder_types = std::tuple<
prop_encoder_type<pp::shared_subscription_available_t, basic::int_def<uint8_t>>,
prop_encoder_type<pp::payload_format_indicator_t, basic::int_def<uint8_t>>,
prop_encoder_type<pp::message_expiry_interval_t, basic::int_def<uint32_t>>,
prop_encoder_type<pp::content_type_t, basic::utf8_def>,
prop_encoder_type<pp::response_topic_t, basic::utf8_def>,
prop_encoder_type<pp::correlation_data_t, basic::utf8_def>,
prop_encoder_type<pp::subscription_identifier_t, basic::int_def<basic::varint_t>>,
prop_encoder_type<pp::session_expiry_interval_t, basic::int_def<uint32_t>>,
prop_encoder_type<pp::assigned_client_identifier_t, basic::utf8_def>,
prop_encoder_type<pp::server_keep_alive_t, basic::int_def<uint16_t>>,
prop_encoder_type<pp::authentication_method_t, basic::utf8_def>,
prop_encoder_type<pp::authentication_data_t, basic::utf8_def>,
prop_encoder_type<pp::request_problem_information_t, basic::int_def<uint8_t>>,
prop_encoder_type<pp::will_delay_interval_t, basic::int_def<uint32_t>>,
prop_encoder_type<pp::request_response_information_t, basic::int_def<uint8_t>>,
prop_encoder_type<pp::response_information_t, basic::utf8_def>,
prop_encoder_type<pp::server_reference_t, basic::utf8_def>,
prop_encoder_type<pp::reason_string_t, basic::utf8_def>,
prop_encoder_type<pp::receive_maximum_t, basic::int_def<uint16_t>>,
prop_encoder_type<pp::topic_alias_maximum_t, basic::int_def<uint16_t>>,
prop_encoder_type<pp::topic_alias_t, basic::int_def<uint16_t>>,
prop_encoder_type<pp::maximum_qos_t, basic::int_def<uint8_t>>,
prop_encoder_type<pp::retain_available_t, basic::int_def<uint8_t>>,
prop_encoder_type<pp::user_property_t, basic::utf8_def>,
prop_encoder_type<pp::maximum_packet_size_t, basic::int_def<uint32_t>>,
prop_encoder_type<pp::wildcard_subscription_available_t, basic::int_def<uint8_t>>,
prop_encoder_type<pp::subscription_identifier_available_t, basic::int_def<uint8_t>>
>;
template <pp::property_type p>
constexpr auto encoder_for_prop = typename std::tuple_element_t<
detail::type_index<p, encoder_types>::value, encoder_types
>::value {};
template <typename T>
auto encoder_for_prop_value(const T& val) {
if constexpr (std::is_same_v<T, uint8_t>)
return basic::int_def<uint8_t>{}(val);
else if constexpr (std::is_same_v<T, uint16_t>)
return basic::int_def<uint16_t>{}(val);
else if constexpr (std::is_same_v<T, int32_t>)
return basic::int_def<basic::varint_t>{}(val);
else if constexpr (std::is_same_v<T, uint32_t>)
return basic::int_def<uint32_t>{}(val);
else if constexpr (std::is_same_v<T, std::string>)
return basic::utf8_def{}(val);
else if constexpr (is_pair<T>)
return encoder_for_prop_value(val.first) &
encoder_for_prop_value(val.second);
}
template <typename T, pp::property_type p, typename Enable = void>
class prop_val;
@ -409,10 +351,8 @@ class prop_val<
T, p,
std::enable_if_t<!is_vector<T> && is_optional<T>>
> : public basic::encoder {
// T is always std::optional
using opt_type = typename boost::remove_cv_ref_t<T>::value_type;
// allows T to be reference type to std::optional
static inline std::optional<opt_type> nulltype;
static inline boost::remove_cv_ref_t<T> nulltype;
T _val;
public:
prop_val(T val) : _val(val) {
@ -422,16 +362,14 @@ public:
size_t byte_size() const {
if (!_val) return 0;
auto sval = encoder_for_prop<p>(_val);
return 1 + sval.byte_size();
return 1 + encoder_for_prop_value(*_val).byte_size();
}
std::string& encode(std::string& s) const {
if (!_val)
return s;
s.push_back(p);
auto sval = encoder_for_prop<p>(_val);
return sval.encode(s);
return encoder_for_prop_value(*_val).encode(s);
}
};
@ -440,7 +378,7 @@ template <
>
class prop_val<
T, p,
std::enable_if_t<is_vector<T>>
std::enable_if_t<is_vector<T> || is_small_vector<T>>
> : public basic::encoder {
// allows T to be reference type to std::vector
static inline boost::remove_cv_ref_t<T> nulltype;
@ -456,16 +394,8 @@ public:
if (_val.empty()) return 0;
size_t total_size = 0;
for (size_t i = 0; i < _val.size() && i + 1 < _val.size(); i += 2) {
auto skey = encoder_for_prop<p>(_val[i]);
size_t key_size = skey.byte_size();
auto sval = encoder_for_prop<p>(_val[i + 1]);
size_t val_size = sval.byte_size();
if (key_size && val_size)
total_size += 1 + key_size + val_size;
}
for (const auto& elem : _val)
total_size += 1 + encoder_for_prop_value(elem).byte_size();
return total_size;
}
@ -474,14 +404,9 @@ public:
if (_val.empty())
return s;
for (size_t i = 0; i < _val.size() && i + 1 < _val.size(); i += 2) {
for (const auto& elem: _val) {
s.push_back(p);
auto skey = encoder_for_prop<p>(_val[i]);
skey.encode(s);
auto sval = encoder_for_prop<p>(_val[i + 1]);
sval.encode(s);
encoder_for_prop_value(elem).encode(s);
}
return s;

View File

@ -4,6 +4,7 @@
#include <optional>
#include <vector>
#include <boost/container/small_vector.hpp>
#include <boost/range/iterator_range_core.hpp>
#include <boost/type_traits/remove_cv_ref.hpp>
@ -30,6 +31,21 @@ constexpr bool is_vector = is_specialization<
boost::remove_cv_ref_t<T>, std::vector
>;
template <typename... Args>
constexpr std::true_type is_small_vector_impl(
boost::container::small_vector_base<Args...> const &
);
constexpr std::false_type is_small_vector_impl( ... );
template <typename T>
constexpr bool is_small_vector =
decltype(is_small_vector_impl(std::declval<T>()))::value;
template <typename T>
constexpr bool is_pair = is_specialization<
boost::remove_cv_ref_t<T>, std::pair
>;
template <typename T>
constexpr bool is_boost_iterator = is_specialization<
boost::remove_cv_ref_t<T>, boost::iterator_range

View File

@ -131,8 +131,8 @@ private:
return client::error::malformed_packet;
auto user_properties = props[prop::user_property];
for (const auto& user_prop: user_properties)
if (validate_mqtt_utf8(user_prop) != validation_result::valid)
for (const auto& user_property: user_properties)
if (!is_valid_string_pair(user_property))
return client::error::malformed_packet;
return error_code {};
}

View File

@ -141,8 +141,8 @@ class endpoints {
public:
template <typename Executor>
endpoints(Executor ex, asio::steady_timer& timer)
: _resolver(ex), _connect_timer(timer)
endpoints(Executor ex, asio::steady_timer& timer) :
_resolver(ex), _connect_timer(timer)
{}
void clone_servers(const endpoints& other) {

View File

@ -379,16 +379,11 @@ private:
return client::error::malformed_packet;
auto user_properties = props[prop::user_property];
for (const auto& user_prop: user_properties)
if (validate_mqtt_utf8(user_prop) != validation_result::valid)
for (const auto& user_property: user_properties)
if (!is_valid_string_pair(user_property))
return client::error::malformed_packet;
auto subscription_identifier = props[prop::subscription_identifier];
if (
subscription_identifier &&
(*subscription_identifier < min_subscription_identifier ||
*subscription_identifier > max_subscription_identifier)
)
if (!props[prop::subscription_identifier].empty())
return client::error::malformed_packet;
auto content_type = props[prop::content_type];

View File

@ -220,8 +220,8 @@ private:
error_code validate_props(const subscribe_props& props) const {
auto user_properties = props[prop::user_property];
for (const auto& user_prop: user_properties)
if (validate_mqtt_utf8(user_prop) != validation_result::valid)
for (const auto& user_property: user_properties)
if (!is_valid_string_pair(user_property))
return client::error::malformed_packet;
auto sub_id = props[prop::subscription_identifier];

View File

@ -178,8 +178,8 @@ private:
return client::error::invalid_topic;
auto user_properties = props[prop::user_property];
for (const auto& user_prop: user_properties)
if (validate_mqtt_utf8(user_prop) != validation_result::valid)
for (const auto& user_property: user_properties)
if (!is_valid_string_pair(user_property))
return client::error::malformed_packet;
return error_code {};
}

View File

@ -368,14 +368,15 @@ public:
*
* \details The return type varies according to the property requested.
* For all properties, the return type will be `std::optional` of their respective value type.
* For `async_mqtt5::prop::user_property`, the return type is `std::vector<std::string>`.
* For `async_mqtt5::prop::user_property`, the return type is
* `std::vector<std::pair<std::string, std::string>>`.
*
* \param prop The \__CONNACK_PROPS\__ property value to retrieve.
*
* \par Example
* \code
* std::optional<std::string> auth_method = client.connack_property(async_mqtt5::prop::authentication_method); // ok
* std::optional<std::string> c_type = client.connack_property(async_mqtt5::prop::content_type); // does not compile, not a CONNAK prop!
* std::optional<std::string> c_type = client.connack_property(async_mqtt5::prop::content_type); // does not compile, not a CONNACK prop!
* \endcode
*
* \see See \__CONNACK_PROPS\__ for all eligible properties.

View File

@ -7,6 +7,8 @@
#include <type_traits>
#include <vector>
#include <boost/container/small_vector.hpp>
namespace async_mqtt5::prop {
enum property_type : uint8_t {
@ -39,9 +41,49 @@ enum property_type : uint8_t {
shared_subscription_available_t = 0x2a
};
class alignas(8) subscription_identifiers :
public boost::container::small_vector<int32_t, 1>
{
using base_type = boost::container::small_vector<int32_t, 1>;
public:
using base_type::base_type;
subscription_identifiers(int32_t val) : base_type { val } {}
bool has_value() const noexcept {
return !empty();
}
int32_t& operator*() noexcept {
return front();
}
int32_t operator*() const noexcept {
return front();
}
void emplace(int32_t val = 0) {
*this = val;
}
int32_t value() const {
return front();
}
int32_t value_or(int32_t default_val) const noexcept {
return empty() ? default_val : front();
}
void reset() noexcept {
clear();
}
};
template <property_type p>
struct property_traits;
using user_property_value_t = std::vector<std::pair<std::string, std::string>>;
#define DEF_PROPERTY_TRAIT(Pname, Ptype) \
template <> \
struct property_traits<Pname##_t> { \
@ -55,7 +97,7 @@ DEF_PROPERTY_TRAIT(message_expiry_interval, std::optional<uint32_t>);
DEF_PROPERTY_TRAIT(content_type, std::optional<std::string>);
DEF_PROPERTY_TRAIT(response_topic, std::optional<std::string>);
DEF_PROPERTY_TRAIT(correlation_data, std::optional<std::string>);
DEF_PROPERTY_TRAIT(subscription_identifier, std::optional<int32_t>);
DEF_PROPERTY_TRAIT(subscription_identifier, subscription_identifiers);
DEF_PROPERTY_TRAIT(session_expiry_interval, std::optional<uint32_t>);
DEF_PROPERTY_TRAIT(assigned_client_identifier, std::optional<std::string>);
DEF_PROPERTY_TRAIT(server_keep_alive, std::optional<uint16_t>);
@ -72,7 +114,7 @@ DEF_PROPERTY_TRAIT(topic_alias_maximum, std::optional<uint16_t>);
DEF_PROPERTY_TRAIT(topic_alias, std::optional<uint16_t>);
DEF_PROPERTY_TRAIT(maximum_qos, std::optional<uint8_t>);
DEF_PROPERTY_TRAIT(retain_available, std::optional<uint8_t>);
DEF_PROPERTY_TRAIT(user_property, std::vector<std::string>);
DEF_PROPERTY_TRAIT(user_property, user_property_value_t);
DEF_PROPERTY_TRAIT(maximum_packet_size, std::optional<uint32_t>);
DEF_PROPERTY_TRAIT(wildcard_subscription_available, std::optional<uint8_t>);
DEF_PROPERTY_TRAIT(subscription_identifier_available, std::optional<uint8_t>);

View File

@ -41,8 +41,8 @@ public:
/// \cond INTERNAL
constexpr reason_code() : _code(0xff) {}
constexpr reason_code(uint8_t code, reason_codes::category cat)
: _code(code), _category(cat)
constexpr reason_code(uint8_t code, reason_codes::category cat) :
_code(code), _category(cat)
{}
constexpr explicit reason_code(uint8_t code) : _code(code) {}

View File

@ -80,7 +80,14 @@ inline std::string to_readable_props(Props props) {
if (v.has_value())
stream << *v << " ";
if constexpr (is_vector<decltype(v)>)
stream << boost::algorithm::join(v, ",");
for (size_t i = 0; i < v.size(); i++) {
if constexpr (is_pair<decltype(v[i])>)
stream << "(" << v[i].first << ", " << v[i].second << ")";
else
stream << v[i];
if (i + 1 < v.size())
stream << ", ";
}
return true;
});
return stream.str();

View File

@ -191,7 +191,8 @@ struct shared_connect_prop_test_data : shared_test_data {
const uint16_t topic_alias_maximum = 12345;
const uint8_t request_response_information = 1;
const uint8_t request_problem_information = 0;
const std::vector<std::string> user_properties = std::vector<std::string>{ "key", "val" };
const std::vector<std::pair<std::string, std::string>> user_properties =
{ { "key", "val" } };
connect_props cprops = create_connect_props();
@ -300,7 +301,8 @@ struct shared_connack_prop_test_data {
const std::string assigned_client_id = "client_id";
const uint16_t topic_alias_max = 128;
const std::string reason_string = "reason string";
const std::vector<std::string> user_properties = std::vector<std::string>{ "key", "val" };
const std::vector<std::pair<std::string, std::string>> user_properties =
{ { "key", "val" } };
const uint8_t wildcard_sub = 1;
const uint8_t sub_id = 1;
const uint8_t shared_sub = 0;

View File

@ -125,6 +125,82 @@ BOOST_FIXTURE_TEST_CASE(receive_publish_qos2, shared_test_data) {
run_test(std::move(broker_side));
}
BOOST_FIXTURE_TEST_CASE(receive_publish_properties, shared_test_data) {
constexpr int expected_handlers_called = 1;
int handlers_called = 0;
publish_props pprops;
pprops[prop::payload_format_indicator] = uint8_t(0);
pprops[prop::message_expiry_interval] = 102u;
pprops[prop::content_type] = "content/type";
pprops[prop::response_topic] = "response/topic";
pprops[prop::correlation_data] = std::string {
static_cast<char>(0x00), static_cast<char>(0x01), static_cast<char>(0xFF)
};
pprops[prop::subscription_identifier] = { 40, 41, 42 };
pprops[prop::topic_alias] = uint16_t(103);
pprops[prop::user_property] = { { "name1", "value1" }, { "name2", "value2 "} };
auto publish = encoders::encode_publish(
1, topic, payload,
qos_e::at_most_once, retain_e::no, dup_e::no,
pprops
);
test::msg_exchange broker_side;
broker_side
.expect(connect)
.complete_with(success, after(0ms))
.reply_with(connack, after(0ms))
.send(publish, after(10ms));
asio::io_context ioc;
auto executor = ioc.get_executor();
auto& broker = asio::make_service<test::test_broker>(
ioc, executor, std::move(broker_side)
);
using client_type = mqtt_client<test::test_stream>;
client_type c(executor);
c.brokers("127.0.0.1")
.async_run(asio::detached);
c.async_receive([&handlers_called, &pprops, &c](
error_code ec, std::string rec_topic, std::string rec_payload,
publish_props rec_pprops
){
++handlers_called;
auto data = shared_test_data();
BOOST_TEST(!ec);
BOOST_TEST(data.topic == rec_topic);
BOOST_TEST(data.payload == rec_payload);
BOOST_TEST(*pprops[prop::payload_format_indicator]
== *rec_pprops[prop::payload_format_indicator]);
BOOST_TEST(*pprops[prop::message_expiry_interval]
== *rec_pprops[prop::message_expiry_interval]);
BOOST_TEST(*pprops[prop::content_type]
== *rec_pprops[prop::content_type]);
BOOST_TEST(*pprops[prop::response_topic]
== *rec_pprops[prop::response_topic]);
BOOST_TEST(*pprops[prop::correlation_data]
== *rec_pprops[prop::correlation_data]);
BOOST_TEST(pprops[prop::subscription_identifier]
== rec_pprops[prop::subscription_identifier]);
BOOST_TEST(*pprops[prop::topic_alias]
== *rec_pprops[prop::topic_alias]);
BOOST_TEST(pprops[prop::user_property]
== rec_pprops[prop::user_property]);
c.cancel();
}
);
ioc.run();
BOOST_TEST(handlers_called == expected_handlers_called);
BOOST_TEST(broker.received_all_expected());
}
BOOST_FIXTURE_TEST_CASE(receive_malformed_publish, shared_test_data) {
// packets
auto malformed_publish = encoders::encode_publish(
@ -322,8 +398,10 @@ BOOST_FIXTURE_TEST_CASE(receive_big_publish, shared_test_data) {
cprops[prop::maximum_packet_size] = 10'000'000;
publish_props big_props;
for (int i = 0; i < 100; i++)
big_props[prop::user_property].push_back(std::string(65534, 'u'));
for (int i = 0; i < 50; i++)
big_props[prop::user_property].emplace_back(
std::string(65534, 'u'), std::string(65534, 'v')
);
// packets
auto connect_big_packets = encoders::encode_connect(

View File

@ -545,16 +545,12 @@ BOOST_FIXTURE_TEST_CASE(send_big_publish, shared_test_data) {
const std::string big_topic = std::string(65534, 't');
const std::string big_payload = std::string(65534, 'p');
publish_props big_props;
for (int i = 0; i < 1; i++)
big_props[prop::user_property].push_back(std::string(65534, 'u'));
// packets
auto allow_big_connack = encoders::encode_connack(false, uint8_t(0x00), cprops);
auto big_publish = encoders::encode_publish(
1, big_topic, big_payload,
qos_e::at_least_once, retain_e::no, dup_e::no,
big_props
publish_props{}
);
auto pingreq = encoders::encode_pingreq();
auto pingresp = encoders::encode_pingresp();
@ -590,7 +586,7 @@ BOOST_FIXTURE_TEST_CASE(send_big_publish, shared_test_data) {
.async_run(asio::detached);
c.async_publish<qos_e::at_least_once>(
big_topic, big_payload, retain_e::no, big_props,
big_topic, big_payload, retain_e::no, publish_props{},
[&handlers_called, &c](error_code ec, reason_code rc, puback_props) {
++handlers_called;

View File

@ -50,9 +50,16 @@ BOOST_AUTO_TEST_CASE(malformed_reason_string) {
run_malformed_props_test(dprops);
}
BOOST_AUTO_TEST_CASE(malformed_user_property) {
BOOST_AUTO_TEST_CASE(malformed_user_property_key) {
disconnect_props dprops;
dprops[prop::user_property].push_back(std::string { 0x01 });
dprops[prop::user_property].emplace_back(std::string { 0x01 }, "value");
run_malformed_props_test(dprops);
}
BOOST_AUTO_TEST_CASE(malformed_user_property_value) {
disconnect_props dprops;
dprops[prop::user_property].emplace_back("key", std::string { 0x01 });
run_malformed_props_test(dprops);
}

View File

@ -110,7 +110,7 @@ BOOST_AUTO_TEST_CASE(malformed_props_1) {
BOOST_AUTO_TEST_CASE(malformed_props_2) {
publish_props pprops;
pprops[prop::user_property].push_back(std::string { 0x01 });
pprops[prop::user_property].emplace_back(std::string { 0x01 }, "value");
run_malformed_props_test(pprops);
}
@ -134,7 +134,7 @@ BOOST_AUTO_TEST_CASE(malformed_props_4) {
BOOST_AUTO_TEST_CASE(malformed_props_5) {
publish_props pprops;
pprops[prop::subscription_identifier] = 300'000'000;
pprops[prop::subscription_identifier].push_back(40);
run_malformed_props_test(pprops);
}

View File

@ -50,8 +50,7 @@ BOOST_AUTO_TEST_CASE(test_connect) {
cprops[prop::topic_alias_maximum] = topic_alias_max;
cprops[prop::request_response_information] = request_response_information;
cprops[prop::request_problem_information] = request_problem_information;
cprops[prop::user_property].emplace_back(user_property_1);
cprops[prop::user_property].emplace_back(user_property_2);
cprops[prop::user_property].emplace_back(user_property_1, user_property_2);
cprops[prop::authentication_method] = auth_method;
cprops[prop::authentication_data] = auth_data;
@ -62,8 +61,7 @@ BOOST_AUTO_TEST_CASE(test_connect) {
w[prop::content_type] = will_content_type;
w[prop::response_topic] = will_response_topic;
w[prop::correlation_data] = will_correlation_data;
w[prop::user_property].emplace_back(will_user_property_1);
w[prop::user_property].emplace_back(will_user_property_2);
w[prop::user_property].emplace_back(will_user_property_1, will_user_property_2);
std::optional<will> will_opt { std::move(w) };
auto msg = encoders::encode_connect(
@ -96,9 +94,9 @@ BOOST_AUTO_TEST_CASE(test_connect) {
BOOST_CHECK_EQUAL(*cprops_[prop::topic_alias_maximum], topic_alias_max);
BOOST_CHECK_EQUAL(*cprops_[prop::request_response_information], request_response_information);
BOOST_CHECK_EQUAL(*cprops_[prop::request_problem_information], request_problem_information);
BOOST_ASSERT(cprops_[prop::user_property].size() == 2);
BOOST_CHECK_EQUAL(cprops_[prop::user_property][0], user_property_1);
BOOST_CHECK_EQUAL(cprops_[prop::user_property][1], user_property_2);
BOOST_ASSERT(cprops_[prop::user_property].size() == 1);
BOOST_CHECK_EQUAL(cprops_[prop::user_property][0].first, user_property_1);
BOOST_CHECK_EQUAL(cprops_[prop::user_property][0].second, user_property_2);
BOOST_CHECK_EQUAL(*cprops_[prop::authentication_method], auth_method);
BOOST_CHECK_EQUAL(*cprops_[prop::authentication_data], auth_data);
@ -114,9 +112,9 @@ BOOST_AUTO_TEST_CASE(test_connect) {
BOOST_CHECK_EQUAL(*(*w_)[prop::content_type], will_content_type);
BOOST_CHECK_EQUAL(*(*w_)[prop::response_topic], will_response_topic);
BOOST_CHECK_EQUAL(*(*w_)[prop::correlation_data], will_correlation_data);
BOOST_ASSERT((*w_)[prop::user_property].size() == 2);
BOOST_CHECK_EQUAL((*w_)[prop::user_property][0], will_user_property_1);
BOOST_CHECK_EQUAL((*w_)[prop::user_property][1], will_user_property_2);
BOOST_ASSERT((*w_)[prop::user_property].size() == 1);
BOOST_CHECK_EQUAL((*w_)[prop::user_property][0].first, will_user_property_1);
BOOST_CHECK_EQUAL((*w_)[prop::user_property][0].second, will_user_property_2);
}
BOOST_AUTO_TEST_CASE(test_connack) {
@ -152,8 +150,7 @@ BOOST_AUTO_TEST_CASE(test_connack) {
cprops[prop::assigned_client_identifier] = assigned_client_id;
cprops[prop::topic_alias_maximum] = topic_alias_max;
cprops[prop::reason_string] = reason_string;
cprops[prop::user_property].push_back(user_property_1);
cprops[prop::user_property].push_back(user_property_2);
cprops[prop::user_property].emplace_back(user_property_1, user_property_2);
cprops[prop::wildcard_subscription_available] = wildcard_sub;
cprops[prop::subscription_identifier_available] = sub_id;
cprops[prop::shared_subscription_available] = shared_sub;
@ -186,9 +183,9 @@ BOOST_AUTO_TEST_CASE(test_connack) {
BOOST_CHECK_EQUAL(*cprops_[prop::assigned_client_identifier], assigned_client_id);
BOOST_CHECK_EQUAL(*cprops_[prop::topic_alias_maximum], topic_alias_max);
BOOST_CHECK_EQUAL(*cprops_[prop::reason_string], reason_string);
BOOST_ASSERT(cprops_[prop::user_property].size() == 2);
BOOST_CHECK_EQUAL(cprops_[prop::user_property][0], user_property_1);
BOOST_CHECK_EQUAL(cprops_[prop::user_property][1], user_property_2);
BOOST_ASSERT(cprops_[prop::user_property].size() == 1);
BOOST_CHECK_EQUAL(cprops_[prop::user_property][0].first, user_property_1);
BOOST_CHECK_EQUAL(cprops_[prop::user_property][0].second, user_property_2);
BOOST_CHECK_EQUAL(*cprops_[prop::wildcard_subscription_available], wildcard_sub);
BOOST_CHECK_EQUAL(*cprops_[prop::subscription_identifier_available], sub_id);
BOOST_CHECK_EQUAL(*cprops_[prop::shared_subscription_available], shared_sub);
@ -221,9 +218,8 @@ BOOST_AUTO_TEST_CASE(test_publish) {
pprops[prop::topic_alias] = topic_alias;
pprops[prop::response_topic] = response_topic;
pprops[prop::correlation_data] = correlation_data;
pprops[prop::user_property].emplace_back(publish_prop_1);
pprops[prop::user_property].emplace_back(publish_prop_2);
pprops[prop::subscription_identifier] = subscription_identifier;
pprops[prop::user_property].emplace_back(publish_prop_1, publish_prop_2);
pprops[prop::subscription_identifier].push_back(subscription_identifier);
pprops[prop::content_type] = content_type;
auto msg = encoders::encode_publish(
@ -252,10 +248,11 @@ BOOST_AUTO_TEST_CASE(test_publish) {
BOOST_CHECK_EQUAL(*pprops_[prop::topic_alias], topic_alias);
BOOST_CHECK_EQUAL(*pprops_[prop::response_topic], response_topic);
BOOST_CHECK_EQUAL(*pprops_[prop::correlation_data], correlation_data);
BOOST_ASSERT(pprops_[prop::user_property].size() == 2);
BOOST_CHECK_EQUAL(pprops_[prop::user_property][0], publish_prop_1);
BOOST_CHECK_EQUAL(pprops_[prop::user_property][1], publish_prop_2);
BOOST_CHECK_EQUAL(*pprops_[prop::subscription_identifier], subscription_identifier);
BOOST_ASSERT(pprops_[prop::user_property].size() == 1);
BOOST_CHECK_EQUAL(pprops_[prop::user_property][0].first, publish_prop_1);
BOOST_CHECK_EQUAL(pprops_[prop::user_property][0].second, publish_prop_2);
BOOST_ASSERT(pprops_[prop::subscription_identifier].size() == 1);
BOOST_CHECK_EQUAL(pprops_[prop::subscription_identifier][0], subscription_identifier);
BOOST_CHECK_EQUAL(*pprops_[prop::content_type], content_type);
}
@ -297,8 +294,7 @@ BOOST_AUTO_TEST_CASE(test_puback) {
puback_props pprops;
pprops[prop::reason_string] = reason_string;
pprops[prop::user_property].emplace_back(user_property_1);
pprops[prop::user_property].emplace_back(user_property_2);
pprops[prop::user_property].emplace_back(user_property_1, user_property_2);
auto msg = encoders::encode_puback(packet_id, reason_code, pprops);
@ -318,9 +314,9 @@ BOOST_AUTO_TEST_CASE(test_puback) {
pprops_.visit([](const auto& p, const auto&) { (void)p; BOOST_ASSERT(p); return true; });
BOOST_CHECK_EQUAL(reason_code_, reason_code);
BOOST_CHECK_EQUAL(*pprops_[prop::reason_string], reason_string);
BOOST_ASSERT(pprops_[prop::user_property].size() == 2);
BOOST_CHECK_EQUAL(pprops_[prop::user_property][0], user_property_1);
BOOST_CHECK_EQUAL(pprops_[prop::user_property][1], user_property_2);
BOOST_ASSERT(pprops_[prop::user_property].size() == 1);
BOOST_CHECK_EQUAL(pprops_[prop::user_property][0].first, user_property_1);
BOOST_CHECK_EQUAL(pprops_[prop::user_property][0].second, user_property_2);
}
BOOST_AUTO_TEST_CASE(test_pubrec) {
@ -334,8 +330,7 @@ BOOST_AUTO_TEST_CASE(test_pubrec) {
pubrec_props pprops;
pprops[prop::reason_string] = reason_string;
pprops[prop::user_property].emplace_back(user_property_1);
pprops[prop::user_property].emplace_back(user_property_2);
pprops[prop::user_property].emplace_back(user_property_1, user_property_2);
auto msg = encoders::encode_pubrec(packet_id, reason_code, pprops);
@ -355,9 +350,9 @@ BOOST_AUTO_TEST_CASE(test_pubrec) {
pprops_.visit([](const auto& p, const auto&) { (void)p; BOOST_ASSERT(p); return true; });
BOOST_CHECK_EQUAL(reason_code_, reason_code);
BOOST_CHECK_EQUAL(*pprops_[prop::reason_string], reason_string);
BOOST_ASSERT(pprops_[prop::user_property].size() == 2);
BOOST_CHECK_EQUAL(pprops_[prop::user_property][0], user_property_1);
BOOST_CHECK_EQUAL(pprops_[prop::user_property][1], user_property_2);
BOOST_ASSERT(pprops_[prop::user_property].size() == 1);
BOOST_CHECK_EQUAL(pprops_[prop::user_property][0].first, user_property_1);
BOOST_CHECK_EQUAL(pprops_[prop::user_property][0].second, user_property_2);
}
BOOST_AUTO_TEST_CASE(test_pubrel) {
@ -371,8 +366,7 @@ BOOST_AUTO_TEST_CASE(test_pubrel) {
pubrel_props pprops;
pprops[prop::reason_string] = reason_string;
pprops[prop::user_property].emplace_back(user_property_1);
pprops[prop::user_property].emplace_back(user_property_2);
pprops[prop::user_property].emplace_back(user_property_1, user_property_2);
auto msg = encoders::encode_pubrel(packet_id, reason_code, pprops);
@ -392,9 +386,9 @@ BOOST_AUTO_TEST_CASE(test_pubrel) {
pprops_.visit([](const auto& p, const auto&) { (void)p; BOOST_ASSERT(p); return true; });
BOOST_CHECK_EQUAL(reason_code_, reason_code);
BOOST_CHECK_EQUAL(*pprops_[prop::reason_string], reason_string);
BOOST_ASSERT(pprops_[prop::user_property].size() == 2);
BOOST_CHECK_EQUAL(pprops_[prop::user_property][0], user_property_1);
BOOST_CHECK_EQUAL(pprops_[prop::user_property][1], user_property_2);
BOOST_ASSERT(pprops_[prop::user_property].size() == 1);
BOOST_CHECK_EQUAL(pprops_[prop::user_property][0].first, user_property_1);
BOOST_CHECK_EQUAL(pprops_[prop::user_property][0].second, user_property_2);
}
BOOST_AUTO_TEST_CASE(test_pubcomp) {
@ -408,8 +402,7 @@ BOOST_AUTO_TEST_CASE(test_pubcomp) {
pubcomp_props pprops;
pprops[prop::reason_string] = reason_string;
pprops[prop::user_property].emplace_back(user_property_1);
pprops[prop::user_property].emplace_back(user_property_2);
pprops[prop::user_property].emplace_back(user_property_1, user_property_2);
auto msg = encoders::encode_pubcomp(packet_id, reason_code, pprops);
@ -429,9 +422,9 @@ BOOST_AUTO_TEST_CASE(test_pubcomp) {
pprops_.visit([](const auto& p, const auto&) { (void)p; BOOST_ASSERT(p); return true; });
BOOST_CHECK_EQUAL(reason_code_, reason_code);
BOOST_CHECK_EQUAL(*pprops_[prop::reason_string], reason_string);
BOOST_ASSERT(pprops_[prop::user_property].size() == 2);
BOOST_CHECK_EQUAL(pprops_[prop::user_property][0], user_property_1);
BOOST_CHECK_EQUAL(pprops_[prop::user_property][1], user_property_2);
BOOST_ASSERT(pprops_[prop::user_property].size() == 1);
BOOST_CHECK_EQUAL(pprops_[prop::user_property][0].first, user_property_1);
BOOST_CHECK_EQUAL(pprops_[prop::user_property][0].second, user_property_2);
}
BOOST_AUTO_TEST_CASE(test_subscribe) {
@ -447,8 +440,7 @@ BOOST_AUTO_TEST_CASE(test_subscribe) {
subscribe_props sprops;
sprops[prop::subscription_identifier] = sub_id;
sprops[prop::user_property].push_back(user_property_1);
sprops[prop::user_property].push_back(user_property_2);
sprops[prop::user_property].emplace_back(user_property_1, user_property_2);
std::vector<subscribe_topic> filters {
{
@ -484,9 +476,9 @@ BOOST_AUTO_TEST_CASE(test_subscribe) {
sprops_.visit([](const auto& p, const auto&) { (void)p; BOOST_ASSERT(p); return true; });
BOOST_CHECK_EQUAL(*sprops_[prop::subscription_identifier], sub_id);
BOOST_ASSERT(sprops_[prop::user_property].size() == 2);
BOOST_CHECK_EQUAL(sprops_[prop::user_property][0], user_property_1);
BOOST_CHECK_EQUAL(sprops_[prop::user_property][1], user_property_2);
BOOST_ASSERT(sprops_[prop::user_property].size() == 1);
BOOST_CHECK_EQUAL(sprops_[prop::user_property][0].first, user_property_1);
BOOST_CHECK_EQUAL(sprops_[prop::user_property][0].second, user_property_2);
}
BOOST_AUTO_TEST_CASE(test_suback) {
@ -500,8 +492,7 @@ BOOST_AUTO_TEST_CASE(test_suback) {
suback_props sprops;
sprops[prop::reason_string] = reason_string;
sprops[prop::user_property].push_back(user_property_1);
sprops[prop::user_property].push_back(user_property_2);
sprops[prop::user_property].emplace_back(user_property_1, user_property_2);
auto msg = encoders::encode_suback(packet_id, reason_codes, sprops);
@ -521,9 +512,9 @@ BOOST_AUTO_TEST_CASE(test_suback) {
sprops_.visit([](const auto& p, const auto&) { (void)p; BOOST_ASSERT(p); return true; });
BOOST_CHECK_EQUAL(*sprops_[prop::reason_string], reason_string);
BOOST_ASSERT(sprops_[prop::user_property].size() == 2);
BOOST_CHECK_EQUAL(sprops_[prop::user_property][0], user_property_1);
BOOST_CHECK_EQUAL(sprops_[prop::user_property][1], user_property_2);
BOOST_ASSERT(sprops_[prop::user_property].size() == 1);
BOOST_CHECK_EQUAL(sprops_[prop::user_property][0].first, user_property_1);
BOOST_CHECK_EQUAL(sprops_[prop::user_property][0].second, user_property_2);
}
BOOST_AUTO_TEST_CASE(test_unsubscribe) {
@ -535,8 +526,7 @@ BOOST_AUTO_TEST_CASE(test_unsubscribe) {
std::string user_property_2 = "UNSUBSCRIBE user prop val";
unsubscribe_props uprops;
uprops[prop::user_property].push_back(user_property_1);
uprops[prop::user_property].push_back(user_property_2);
uprops[prop::user_property].emplace_back(user_property_1, user_property_2);
auto msg = encoders::encode_unsubscribe(packet_id, topics, uprops);
@ -555,9 +545,9 @@ BOOST_AUTO_TEST_CASE(test_unsubscribe) {
BOOST_CHECK(topics_ == topics);
uprops_.visit([](const auto& p, const auto&) { (void)p; BOOST_ASSERT(p); return true; });
BOOST_ASSERT(uprops_[prop::user_property].size() == 2);
BOOST_CHECK_EQUAL(uprops_[prop::user_property][0], user_property_1);
BOOST_CHECK_EQUAL(uprops_[prop::user_property][1], user_property_2);
BOOST_ASSERT(uprops_[prop::user_property].size() == 1);
BOOST_CHECK_EQUAL(uprops_[prop::user_property][0].first, user_property_1);
BOOST_CHECK_EQUAL(uprops_[prop::user_property][0].second, user_property_2);
}
BOOST_AUTO_TEST_CASE(test_unsuback) {
@ -571,8 +561,7 @@ BOOST_AUTO_TEST_CASE(test_unsuback) {
unsuback_props uprops;
uprops[prop::reason_string] = reason_string;
uprops[prop::user_property].push_back(user_property_1);
uprops[prop::user_property].push_back(user_property_2);
uprops[prop::user_property].emplace_back(user_property_1, user_property_2);
auto msg = encoders::encode_unsuback(packet_id, reason_codes, uprops);
@ -592,9 +581,9 @@ BOOST_AUTO_TEST_CASE(test_unsuback) {
uprops_.visit([](const auto& p, const auto&) { (void)p; BOOST_ASSERT(p); return true; });
BOOST_CHECK_EQUAL(*uprops_[prop::reason_string], reason_string);
BOOST_ASSERT(uprops_[prop::user_property].size() == 2);
BOOST_CHECK_EQUAL(uprops_[prop::user_property][0], user_property_1);
BOOST_CHECK_EQUAL(uprops_[prop::user_property][1], user_property_2);
BOOST_ASSERT(uprops_[prop::user_property].size() == 1);
BOOST_CHECK_EQUAL(uprops_[prop::user_property][0].first, user_property_1);
BOOST_CHECK_EQUAL(uprops_[prop::user_property][0].second, user_property_2);
}
BOOST_AUTO_TEST_CASE(test_disconnect) {
@ -610,8 +599,7 @@ BOOST_AUTO_TEST_CASE(test_disconnect) {
disconnect_props dprops;
dprops[prop::session_expiry_interval] = session_expiry_interval;
dprops[prop::reason_string] = reason_string;
dprops[prop::user_property].emplace_back(user_property_1);
dprops[prop::user_property].emplace_back(user_property_2);
dprops[prop::user_property].emplace_back(user_property_1, user_property_2);
dprops[prop::server_reference] = server_reference;
auto msg = encoders::encode_disconnect(reason_code, dprops);
@ -630,9 +618,9 @@ BOOST_AUTO_TEST_CASE(test_disconnect) {
dprops_.visit([](const auto& p, const auto&) { (void)p; BOOST_ASSERT(p); return true; });
BOOST_CHECK_EQUAL(*dprops_[prop::session_expiry_interval], session_expiry_interval);
BOOST_CHECK_EQUAL(*dprops_[prop::reason_string], reason_string);
BOOST_ASSERT(dprops_[prop::user_property].size() == 2);
BOOST_CHECK_EQUAL(dprops_[prop::user_property][0], user_property_1);
BOOST_CHECK_EQUAL(dprops_[prop::user_property][1], user_property_2);
BOOST_ASSERT(dprops_[prop::user_property].size() == 1);
BOOST_CHECK_EQUAL(dprops_[prop::user_property][0].first, user_property_1);
BOOST_CHECK_EQUAL(dprops_[prop::user_property][0].second, user_property_2);
BOOST_CHECK_EQUAL(*dprops_[prop::server_reference], server_reference);
}
@ -651,8 +639,7 @@ BOOST_AUTO_TEST_CASE(test_auth) {
aprops[prop::authentication_method] = authentication_method;
aprops[prop::authentication_data] = authentication_data;
aprops[prop::reason_string] = reason_string;
aprops[prop::user_property].emplace_back(user_property_1);
aprops[prop::user_property].emplace_back(user_property_2);
aprops[prop::user_property].emplace_back(user_property_1, user_property_2);
auto msg = encoders::encode_auth(reason_code, aprops);
@ -671,9 +658,9 @@ BOOST_AUTO_TEST_CASE(test_auth) {
BOOST_CHECK_EQUAL(*aprops_[prop::authentication_method], authentication_method);
BOOST_CHECK_EQUAL(*aprops_[prop::authentication_data], authentication_data);
BOOST_CHECK_EQUAL(*aprops_[prop::reason_string], reason_string);
BOOST_ASSERT(aprops_[prop::user_property].size() == 2);
BOOST_CHECK_EQUAL(aprops_[prop::user_property][0], user_property_1);
BOOST_CHECK_EQUAL(aprops_[prop::user_property][1], user_property_2);
BOOST_ASSERT(aprops_[prop::user_property].size() == 1);
BOOST_CHECK_EQUAL(aprops_[prop::user_property][0].first, user_property_1);
BOOST_CHECK_EQUAL(aprops_[prop::user_property][0].second, user_property_2);
}
BOOST_AUTO_TEST_CASE(test_pingreq) {
@ -692,8 +679,7 @@ BOOST_AUTO_TEST_CASE(test_pingresp) {
BOOST_AUTO_TEST_CASE(empty_user_property) {
publish_props pprops;
pprops[prop::user_property].emplace_back("");
pprops[prop::user_property].emplace_back("");
pprops[prop::user_property].emplace_back("", "");
auto msg = encoders::encode_publish(
1, "topic", "payload",
@ -712,69 +698,9 @@ BOOST_AUTO_TEST_CASE(empty_user_property) {
const auto& [topic_, packet_id_, flags, pprops_, payload_] = *rv;
auto user_props_ = pprops_[prop::user_property];
BOOST_ASSERT(user_props_.size() == 2);
BOOST_CHECK_EQUAL(user_props_[0], "");
BOOST_CHECK_EQUAL(user_props_[1], "");
}
BOOST_AUTO_TEST_CASE(omit_invalid_user_property) {
// testing variables
std::string_view user_property_1 = "key";
std::string_view user_property_2 = "val";
std::string_view user_property_3 = "lone key";
publish_props pprops;
pprops[prop::user_property].emplace_back(user_property_1);
pprops[prop::user_property].emplace_back(user_property_2);
pprops[prop::user_property].emplace_back(user_property_3);
auto msg = encoders::encode_publish(
1, "topic", "payload",
qos_e::at_least_once, retain_e::yes, dup_e::no,
pprops
);
byte_citer it = msg.cbegin(), last = msg.cend();
auto header = decoders::decode_fixed_header(it, last);
BOOST_ASSERT(header);
const auto& [control_byte, remain_length] = *header;
auto rv = decoders::decode_publish(control_byte, remain_length, it);
BOOST_ASSERT(rv);
const auto& [topic_, packet_id_, flags, pprops_, payload_] = *rv;
auto user_props_ = pprops_[prop::user_property];
BOOST_ASSERT(user_props_.size() == 2);
BOOST_CHECK_EQUAL(user_props_[0], user_property_1);
BOOST_CHECK_EQUAL(user_props_[1], user_property_2);
}
BOOST_AUTO_TEST_CASE(omit_all_user_property) {
// testing variables
std::string_view user_property_1 = "key";
publish_props pprops;
pprops[prop::user_property].emplace_back(user_property_1);
auto msg = encoders::encode_publish(
1, "topic", "payload",
qos_e::at_least_once, retain_e::yes, dup_e::no,
pprops
);
byte_citer it = msg.cbegin(), last = msg.cend();
auto header = decoders::decode_fixed_header(it, last);
BOOST_ASSERT(header);
const auto& [control_byte, remain_length] = *header;
auto rv = decoders::decode_publish(control_byte, remain_length, it);
BOOST_ASSERT(rv);
const auto& [topic_, packet_id_, flags, pprops_, payload_] = *rv;
auto user_props_ = pprops_[prop::user_property];
BOOST_CHECK(user_props_.size() == 0);
BOOST_ASSERT(user_props_.size() == 1);
BOOST_CHECK_EQUAL(user_props_[0].first, "");
BOOST_CHECK_EQUAL(user_props_[0].second, "");
}
BOOST_AUTO_TEST_CASE(deserialize_user_property) {
@ -795,9 +721,9 @@ BOOST_AUTO_TEST_CASE(deserialize_user_property) {
const auto& [reason_code_, pprops_] = *rv;
auto user_props_ = pprops_[prop::user_property];
BOOST_ASSERT(user_props_.size() == 2);
BOOST_CHECK_EQUAL(user_props_[0], "key");
BOOST_CHECK_EQUAL(user_props_[1], "val");
BOOST_ASSERT(user_props_.size() == 1);
BOOST_CHECK_EQUAL(user_props_[0].first, "key");
BOOST_CHECK_EQUAL(user_props_[0].second, "val");
}
BOOST_AUTO_TEST_CASE(deserialize_empty_user_property) {
@ -818,9 +744,9 @@ BOOST_AUTO_TEST_CASE(deserialize_empty_user_property) {
const auto& [reason_code_, pprops_] = *rv;
auto user_props_ = pprops_[prop::user_property];
BOOST_ASSERT(user_props_.size() == 2);
BOOST_CHECK_EQUAL(user_props_[0], "");
BOOST_CHECK_EQUAL(user_props_[1], "");
BOOST_ASSERT(user_props_.size() == 1);
BOOST_CHECK_EQUAL(user_props_[0].first, "");
BOOST_CHECK_EQUAL(user_props_[0].second, "");
}
BOOST_AUTO_TEST_CASE(malformed_user_property) {

View File

@ -105,14 +105,14 @@ BOOST_AUTO_TEST_CASE(invalid_topic_filter_6) {
BOOST_AUTO_TEST_CASE(malformed_user_property_1) {
subscribe_props sprops;
sprops[prop::user_property].push_back(std::string(10, char(0x01)));
sprops[prop::user_property].emplace_back("key", std::string(10, char(0x01)));
run_test(client::error::malformed_packet, "topic", sprops);
}
BOOST_AUTO_TEST_CASE(malformed_user_property_2) {
subscribe_props sprops;
sprops[prop::user_property].push_back(std::string(75000, 'a'));
sprops[prop::user_property].emplace_back("key", std::string(75000, 'a'));
run_test(client::error::malformed_packet, "topic", sprops);
}

View File

@ -100,14 +100,14 @@ BOOST_AUTO_TEST_CASE(invalid_topic_filter_6) {
BOOST_AUTO_TEST_CASE(malformed_user_property_1) {
unsubscribe_props uprops;
uprops[prop::user_property].push_back(std::string(10, char(0x01)));
uprops[prop::user_property].emplace_back("key", std::string(10, char(0x01)));
run_test(client::error::malformed_packet, "topic", uprops);
}
BOOST_AUTO_TEST_CASE(malformed_user_property_2) {
unsubscribe_props uprops;
uprops[prop::user_property].push_back(std::string(75000, 'a'));
uprops[prop::user_property].emplace_back("key", std::string(75000, 'a'));
run_test(client::error::malformed_packet, "topic", uprops);
}