async_mqtt 4.1.0
Loading...
Searching...
No Matches
v3_1_1_connect.hpp
1// Copyright Takatoshi Kondo 2022
2//
3// Distributed under the Boost Software License, Version 1.0.
4// (See accompanying file LICENSE_1_0.txt or copy at
5// http://www.boost.org/LICENSE_1_0.txt)
6
7#if !defined(ASYNC_MQTT_PACKET_V3_1_1_CONNECT_HPP)
8#define ASYNC_MQTT_PACKET_V3_1_1_CONNECT_HPP
9
10#include <utility>
11#include <numeric>
12
13#include <boost/numeric/conversion/cast.hpp>
14
15#include <async_mqtt/exception.hpp>
16#include <async_mqtt/buffer.hpp>
17#include <async_mqtt/variable_bytes.hpp>
18
19#include <async_mqtt/util/move.hpp>
20#include <async_mqtt/util/static_vector.hpp>
21#include <async_mqtt/util/endian_convert.hpp>
22#include <async_mqtt/util/utf8validate.hpp>
23
24#include <async_mqtt/packet/fixed_header.hpp>
25#include <async_mqtt/packet/copy_to_static_vector.hpp>
26#include <async_mqtt/packet/connect_flags.hpp>
27#include <async_mqtt/packet/will.hpp>
28
29namespace async_mqtt::v3_1_1 {
30
31namespace as = boost::asio;
32
40public:
68 bool clean_session,
69 std::uint16_t keep_alive_sec,
76 force_move(client_id),
77 nullopt,
78 force_move(user_name),
79 force_move(password)
80 )
81 {}
111 bool clean_session,
112 std::uint16_t keep_alive_sec,
115 optional<buffer> user_name = nullopt,
116 optional<buffer> password = nullopt
117 )
118 : fixed_header_{
119 make_fixed_header(control_packet_type::connect, 0b0000)
120 },
121 connect_flags_{0},
122 // protocol name length, protocol name, protocol level, connect flag, client id length, client id, keep alive
123 remaining_length_(
124 2 + // protocol name length
125 4 + // protocol name
126 1 + // protocol level
127 1 + // connect flag
128 2 + // keep alive
129 2 + // client id length
130 client_id.size() // client id
131 ),
132 protocol_name_and_level_{0x00, 0x04, 'M', 'Q', 'T', 'T', 0x04},
133 client_id_{force_move(client_id)},
134 client_id_length_buf_(2),
135 keep_alive_buf_(2)
136 {
137 endian_store(keep_alive_sec, keep_alive_buf_.data());
138 endian_store(boost::numeric_cast<std::uint16_t>(client_id_.size()), client_id_length_buf_.data());
139
140 if (!utf8string_check(client_id_)) {
141 throw make_error(
142 errc::bad_message,
143 "v3_1_1::connect_packet client_id invalid utf8"
144 );
145 }
146
147 if (clean_session) connect_flags_ |= connect_flags::mask_clean_session;
148 if (user_name) {
149 if (!utf8string_check(*user_name)) {
150 throw make_error(
151 errc::bad_message,
152 "v3_1_1::connect_packet user name invalid utf8"
153 );
154 }
155 connect_flags_ |= connect_flags::mask_user_name_flag;
156 user_name_ = force_move(*user_name);
157 user_name_length_buf_ = endian_static_vector(boost::numeric_cast<std::uint16_t>(user_name_.size()));
158 remaining_length_ += 2 + user_name_.size();
159 }
160 if (password) {
161 connect_flags_ |= connect_flags::mask_password_flag;
162 password_ = force_move(*password);
163 password_length_buf_ = endian_static_vector(boost::numeric_cast<std::uint16_t>(password_.size()));
164 remaining_length_ += 2 + password_.size();
165 }
166 if (w) {
167 connect_flags_ |= connect_flags::mask_will_flag;
168 if (w->get_retain() == pub::retain::yes) connect_flags_ |= connect_flags::mask_will_retain;
169 connect_flags::set_will_qos(connect_flags_, w->get_qos());
170 if (!utf8string_check(w->topic())) {
171 throw make_error(
172 errc::bad_message,
173 "v3_1_1::connect_packet will topic invalid utf8"
174 );
175 }
176 will_topic_ = force_move(w->topic());
177 will_topic_length_buf_ = endian_static_vector(boost::numeric_cast<std::uint16_t>(will_topic_.size()));
178 if (w->message().size() > 0xffffL) {
179 throw make_error(
180 errc::bad_message,
181 "v3_1_1::connect_packet will message too long"
182 );
183 }
184 will_message_ = force_move(w->message());
185 will_message_length_buf_ = endian_static_vector(boost::numeric_cast<std::uint16_t>(will_message_.size()));
186
187 remaining_length_ += 2 + will_topic_.size() + 2 + will_message_.size();
188 }
189
190 auto rb = val_to_variable_bytes(boost::numeric_cast<std::uint32_t>(remaining_length_));
191 for (auto e : rb) {
192 remaining_length_buf_.push_back(e);
193 }
194 }
195
197 // fixed_header
198 if (buf.empty()) {
199 throw make_error(
200 errc::bad_message,
201 "v3_1_1::connect_packet fixed_header doesn't exist"
202 );
203 }
204 fixed_header_ = static_cast<std::uint8_t>(buf.front());
205 buf.remove_prefix(1);
206 auto cpt_opt = get_control_packet_type_with_check(fixed_header_);
207 if (!cpt_opt || *cpt_opt != control_packet_type::connect) {
208 throw make_error(
209 errc::bad_message,
210 "v3_1_1::connect_packet fixed_header is invalid"
211 );
212 }
213
214 // remaining_length
215 if (auto vl_opt = insert_advance_variable_length(buf, remaining_length_buf_)) {
216 remaining_length_ = *vl_opt;
217 }
218 else {
219 throw make_error(errc::bad_message, "v3_1_1::connect_packet remaining length is invalid");
220 }
221 if (remaining_length_ != buf.size()) {
222 throw make_error(errc::bad_message, "v3_1_1::connect_packet remaining length doesn't match buf.size()");
223 }
224
225 // protocol name and level
226 if (!insert_advance(buf, protocol_name_and_level_)) {
227 throw make_error(
228 errc::bad_message,
229 "v3_1_1::connect_packet length of protocol_name or level is invalid"
230 );
231 }
232 static_vector<char, 7> expected_protocol_name_and_level {
233 0, 4, 'M', 'Q', 'T', 'T', 4
234 };
235 if (protocol_name_and_level_ != expected_protocol_name_and_level) {
236 throw make_error(
237 errc::bad_message,
238 "v3_1_1::connect_packet contents of protocol_name or level is invalid"
239 );
240 }
241
242 // connect_flags
243 if (buf.size() < 1) {
244 throw make_error(
245 errc::bad_message,
246 "v3_1_1::connect_packet connect_flags doesn't exist"
247 );
248 }
249 connect_flags_ = buf.front();
250 if (connect_flags_ & 0b00000001) {
251 throw make_error(
252 errc::bad_message,
253 "v3_1_1::connect_packet connect_flags reserved bit0 is 1 (must be 0)"
254 );
255 }
256 buf.remove_prefix(1);
257
258 // keep_alive
259 if (!insert_advance(buf, keep_alive_buf_)) {
260 throw make_error(
261 errc::bad_message,
262 "v3_1_1::connect_packet keep_alive is invalid"
263 );
264 }
265
266 // client_id_length
267 if (!insert_advance(buf, client_id_length_buf_)) {
268 throw make_error(
269 errc::bad_message,
270 "v3_1_1::connect_packet length of client_id is invalid"
271 );
272 }
273 auto client_id_length = endian_load<std::uint16_t>(client_id_length_buf_.data());
274
275 // client_id
276 if (buf.size() < client_id_length) {
277 throw make_error(
278 errc::bad_message,
279 "v3_1_1::connect_packet client_id doesn't match its length"
280 );
281 }
282 client_id_ = buf.substr(0, client_id_length);
283 if (!utf8string_check(client_id_)) {
284 throw make_error(
285 errc::bad_message,
286 "v3_1_1::connect_packet client_id invalid utf8"
287 );
288 }
289 buf.remove_prefix(client_id_length);
290
291 // will
292 if (connect_flags::has_will_flag(connect_flags_)) {
293 auto will_qos = connect_flags::will_qos(connect_flags_);
294 if (will_qos != qos::at_most_once &&
295 will_qos != qos::at_least_once &&
296 will_qos != qos::exactly_once) {
297 throw make_error(
298 errc::bad_message,
299 "v3_1_1::connect_packet will_qos is invalid"
300 );
301 }
302 // will_topic_length
303 if (!insert_advance(buf, will_topic_length_buf_)) {
304 throw make_error(
305 errc::bad_message,
306 "v3_1_1::connect_packet length of will_topic is invalid"
307 );
308 }
309 auto will_topic_length = endian_load<std::uint16_t>(will_topic_length_buf_.data());
310
311 // will_topic
312 if (buf.size() < will_topic_length) {
313 throw make_error(
314 errc::bad_message,
315 "v3_1_1::connect_packet will_topic doesn't match its length"
316 );
317 }
318 will_topic_ = buf.substr(0, will_topic_length);
319 if (!utf8string_check(will_topic_)) {
320 throw make_error(
321 errc::bad_message,
322 "v3_1_1::connect_packet will topic invalid utf8"
323 );
324 }
325 buf.remove_prefix(will_topic_length);
326
327 // will_message_length
328 if (!insert_advance(buf, will_message_length_buf_)) {
329 throw make_error(
330 errc::bad_message,
331 "v3_1_1::connect_packet length of will_message is invalid"
332 );
333 }
334 auto will_message_length = endian_load<std::uint16_t>(will_message_length_buf_.data());
335
336 // will_message
337 if (buf.size() < will_message_length) {
338 throw make_error(
339 errc::bad_message,
340 "v3_1_1::connect_packet will_message doesn't match its length"
341 );
342 }
343 will_message_ = buf.substr(0, will_message_length);
344 buf.remove_prefix(will_message_length);
345 }
346 else {
347 auto will_retain = connect_flags::will_retain(connect_flags_);
348 auto will_qos = connect_flags::will_qos(connect_flags_);
349 if (will_retain == pub::retain::yes) {
350 throw make_error(
351 errc::bad_message,
352 "v3_1_1::connect_packet combination of will_flag and will_retain is invalid"
353 );
354 }
355 if (will_qos != qos::at_most_once) {
356 throw make_error(
357 errc::bad_message,
358 "v3_1_1::connect_packet combination of will_flag and will_qos is invalid"
359 );
360 }
361 }
362 // user_name
363 if (connect_flags::has_user_name_flag(connect_flags_)) {
364 // user_name_topic_name_length
365 if (!insert_advance(buf, user_name_length_buf_)) {
366 throw make_error(
367 errc::bad_message,
368 "v3_1_1::connect_packet length of user_name is invalid"
369 );
370 }
371 auto user_name_length = endian_load<std::uint16_t>(user_name_length_buf_.data());
372
373 // user_name
374 if (buf.size() < user_name_length) {
375 throw make_error(
376 errc::bad_message,
377 "v3_1_1::connect_packet user_name doesn't match its length"
378 );
379 }
380 user_name_ = buf.substr(0, user_name_length);
381 if (!utf8string_check(user_name_)) {
382 throw make_error(
383 errc::bad_message,
384 "v3_1_1::connect_packet user name invalid utf8"
385 );
386 }
387 buf.remove_prefix(user_name_length);
388 }
389
390 // password
391 if (connect_flags::has_password_flag(connect_flags_)) {
392 // password_topic_name_length
393 if (!insert_advance(buf, password_length_buf_)) {
394 throw make_error(
395 errc::bad_message,
396 "v3_1_1::connect_packet length of password is invalid"
397 );
398 }
399 auto password_length = endian_load<std::uint16_t>(password_length_buf_.data());
400
401 // password
402 if (buf.size() != password_length) {
403 throw make_error(
404 errc::bad_message,
405 "v3_1_1::connect_packet password doesn't match its length"
406 );
407 }
408 password_ = buf.substr(0, password_length);
409 buf.remove_prefix(password_length);
410 }
411 }
412
413 constexpr control_packet_type type() const {
414 return control_packet_type::connect;
415 }
416
422 std::vector<as::const_buffer> const_buffer_sequence() const {
423 std::vector<as::const_buffer> ret;
425
426 ret.emplace_back(as::buffer(&fixed_header_, 1));
427 ret.emplace_back(as::buffer(remaining_length_buf_.data(), remaining_length_buf_.size()));
428 ret.emplace_back(as::buffer(protocol_name_and_level_.data(), protocol_name_and_level_.size()));
429 ret.emplace_back(as::buffer(&connect_flags_, 1));
430 ret.emplace_back(as::buffer(keep_alive_buf_.data(), keep_alive_buf_.size()));
431
432 ret.emplace_back(as::buffer(client_id_length_buf_.data(), client_id_length_buf_.size()));
433 ret.emplace_back(as::buffer(client_id_));
434
435 if (connect_flags::has_will_flag(connect_flags_)) {
436 ret.emplace_back(as::buffer(will_topic_length_buf_.data(), will_topic_length_buf_.size()));
437 ret.emplace_back(as::buffer(will_topic_));
438 ret.emplace_back(as::buffer(will_message_length_buf_.data(), will_message_length_buf_.size()));
439 ret.emplace_back(as::buffer(will_message_));
440 }
441
442 if (connect_flags::has_user_name_flag(connect_flags_)) {
443 ret.emplace_back(as::buffer(user_name_length_buf_.data(), user_name_length_buf_.size()));
444 ret.emplace_back(as::buffer(user_name_));
445 }
446
447 if (connect_flags::has_password_flag(connect_flags_)) {
448 ret.emplace_back(as::buffer(password_length_buf_.data(), password_length_buf_.size()));
449 ret.emplace_back(as::buffer(password_));
450 }
451
452 return ret;
453 }
454
459 std::size_t size() const {
460 return
461 1 + // fixed header
462 remaining_length_buf_.size() +
463 remaining_length_;
464 }
465
470 static constexpr std::size_t num_of_const_buffer_sequence() {
471 return
472 1 + // fixed header
473 1 + // remaining length
474 1 + // protocol name and level
475 1 + // connect flags
476 1 + // keep alive
477
478 2 + // client id length, client id
479
480 2 + // will topic name length, will topic name
481 2 + // will message length, will message
482 2 + // user name length, user name
483 2; // password length, password
484 }
485
490 bool clean_session() const {
491 return connect_flags::has_clean_session(connect_flags_);
492 }
493
498 std::uint16_t keep_alive() const {
499 return endian_load<std::uint16_t>(keep_alive_buf_.data());
500 }
501
507 return client_id_;
508 }
509
515 if (connect_flags::has_user_name_flag(connect_flags_)) {
516 return user_name_;
517 }
518 else {
519 return nullopt;
520 }
521 }
522
528 if (connect_flags::has_password_flag(connect_flags_)) {
529 return password_;
530 }
531 else {
532 return nullopt;
533 }
534 }
535
541 if (connect_flags::has_will_flag(connect_flags_)) {
542 pub::opts opts =
543 connect_flags::will_retain(connect_flags_) |
544 connect_flags::will_qos(connect_flags_);
545 return
546 async_mqtt::will{
547 will_topic_,
548 will_message_,
549 opts,
550 properties{}
551 };
552 }
553 else {
554 return nullopt;
555 }
556 }
557
558private:
559 std::uint8_t fixed_header_;
560 char connect_flags_;
561
562 std::size_t remaining_length_;
563 static_vector<char, 4> remaining_length_buf_;
564
565 static_vector<char, 7> protocol_name_and_level_;
566 buffer client_id_;
567 static_vector<char, 2> client_id_length_buf_;
568
569 buffer will_topic_;
570 static_vector<char, 2> will_topic_length_buf_;
571 buffer will_message_;
572 static_vector<char, 2> will_message_length_buf_;
573
574 buffer user_name_;
575 static_vector<char, 2> user_name_length_buf_;
576 buffer password_;
577 static_vector<char, 2> password_length_buf_;
578
579 static_vector<char, 2> keep_alive_buf_;
580};
581
582inline std::ostream& operator<<(std::ostream& o, connect_packet const& v) {
583 o <<
584 "v3_1_1::connect{" <<
585 "cid:" << v.client_id() << "," <<
586 "ka:" << v.keep_alive() << "," <<
587 "cs:" << v.clean_session();
588 if (v.user_name()) {
589 o << ",un:" << *v.user_name();
590 }
591 if (v.password()) {
592 o << ",pw:" << "*****";
593 }
594 if (v.get_will()) {
595 o << ",will:" << *v.get_will();
596 }
597 o << "}";
598 return o;
599}
600
601} // namespace async_mqtt::v3_1_1
602
603#endif // ASYNC_MQTT_PACKET_V3_1_1_CONNECT_HPP
Definition packet_variant.hpp:49
buffer that has string_view interface This class provides string_view interface. This class hold stri...
Definition buffer.hpp:30
buffer substr(size_type pos=0, size_type count=npos) const &
get substring The returned buffer ragnge is the same as string_view::substr(). In addition the lifeti...
Definition buffer.hpp:201
MQTT CONNECT packet (v3.1.1)
Definition v3_1_1_connect.hpp:39
static constexpr std::size_t num_of_const_buffer_sequence()
Get number of element of const_buffer_sequence.
Definition v3_1_1_connect.hpp:470
optional< buffer > user_name() const
Get user_name.
Definition v3_1_1_connect.hpp:514
connect_packet(bool clean_session, std::uint16_t keep_alive_sec, buffer client_id, optional< buffer > user_name=nullopt, optional< buffer > password=nullopt)
Definition v3_1_1_connect.hpp:67
std::size_t size() const
Get packet size.
Definition v3_1_1_connect.hpp:459
optional< buffer > password() const
Get password.
Definition v3_1_1_connect.hpp:527
connect_packet(bool clean_session, std::uint16_t keep_alive_sec, buffer client_id, optional< will > w, optional< buffer > user_name=nullopt, optional< buffer > password=nullopt)
Definition v3_1_1_connect.hpp:110
std::vector< as::const_buffer > const_buffer_sequence() const
Create const buffer sequence it is for boost asio APIs.
Definition v3_1_1_connect.hpp:422
bool clean_session() const
Get clean_session.
Definition v3_1_1_connect.hpp:490
std::uint16_t keep_alive() const
Get keep_alive.
Definition v3_1_1_connect.hpp:498
optional< will > get_will() const
Get will.
Definition v3_1_1_connect.hpp:540
buffer client_id() const
Get client_id.
Definition v3_1_1_connect.hpp:506
MQTT PublishOptions.
Definition pubopts.hpp:87