12345678910111213141516171819202122232425262728293031323334353637383940414243444546474849505152535455565758596061626364656667686970717273747576777879808182838485868788899091929394959697989910010110210310410510610710810911011111211311411511611711811912012112212312412512612712812913013113213313413513613713813914014114214314414514614714814915015115215315415515615715815916016116216316416516616716816917017117217317417517617717817918018118218318418518618718818919019119219319419519619719819920020120220320420520620720820921021121221321421521621721821922022122222322422522622722822923023123223323423523623723823924024124224324424524624724824925025125225325425525625725825926026126226326426526626726826927027127227327427527627727827928028128228328428528628728828929029129229329429529629729829930030130230330430530630730830931031131231331431531631731831932032132232332432532632732832933033133233333433533633733833934034134234334434534634734834935035135235335435535635735835936036136236336436536636736836937037137237337437537637737837938038138238338438538638738838939039139239339439539639739839940040140240340440540640740840941041141241341441541641741841942042142242342442542642742842943043143243343443543643743843944044144244344444544644744844945045145245345445545645745845946046146246346446546646746846947047147247347447547647747847948048148248348448548648748848949049149249349449549649749849950050150250350450550650750850951051151251351451551651751851952052152252352452552652752852953053153253353453553653753853954054154254354454554654754854955055155255355455555655755855956056156256356456556656756856957057157257357457557657757857958058158258358458558658758858959059159259359459559659759859960060160260360460560660760860961061161261361461561661761861962062162262362462562662762862963063163263363463563663763863964064164264364464564664764864965065165265365465565665765865966066166266366466566666766866967067167267367467567667767867968068168268368468568668768868969069169269369469569669769869970070170270370470570670770870971071171271371471571671771871972072172272372472572672772872973073173273373473573673773873974074174274374474574674774874975075175275375475575675775875976076176276376476576676776876977077177277377477577677777877978078178278378478578678778878979079179279379479579679779879980080180280380480580680780880981081181281381481581681781881982082182282382482582682782882983083183283383483583683783883984084184284384484584684784884985085185285385485585685785885986086186286386486586686786886987087187287387487587687787887988088188288388488588688788888989089189289389489589689789889990090190290390490590690790890991091191291391491591691791891992092192292392492592692792892993093193293393493593693793893994094194294394494594694794894995095195295395495595695795895996096196296396496596696796896997097197297397497597697797897998098198298398498598698798898999099199299399499599699799899910001001100210031004100510061007100810091010101110121013101410151016101710181019102010211022102310241025102610271028102910301031103210331034103510361037103810391040104110421043104410451046104710481049105010511052105310541055105610571058105910601061106210631064106510661067106810691070107110721073107410751076107710781079108010811082108310841085108610871088108910901091109210931094109510961097109810991100110111021103110411051106110711081109111011111112111311141115111611171118111911201121112211231124112511261127112811291130113111321133113411351136113711381139114011411142114311441145114611471148114911501151115211531154115511561157115811591160116111621163116411651166116711681169117011711172117311741175117611771178117911801181118211831184118511861187118811891190119111921193119411951196119711981199120012011202120312041205120612071208120912101211121212131214121512161217121812191220122112221223122412251226122712281229123012311232123312341235123612371238123912401241124212431244124512461247124812491250125112521253125412551256125712581259126012611262126312641265126612671268126912701271127212731274127512761277127812791280128112821283128412851286128712881289129012911292129312941295129612971298129913001301130213031304130513061307130813091310131113121313131413151316131713181319132013211322132313241325132613271328132913301331133213331334133513361337133813391340134113421343134413451346134713481349135013511352135313541355135613571358135913601361136213631364136513661367136813691370137113721373137413751376137713781379138013811382138313841385138613871388138913901391139213931394139513961397139813991400140114021403140414051406140714081409141014111412141314141415141614171418141914201421142214231424142514261427142814291430143114321433143414351436143714381439144014411442144314441445144614471448144914501451145214531454145514561457145814591460146114621463146414651466146714681469147014711472147314741475147614771478147914801481148214831484148514861487148814891490149114921493149414951496149714981499150015011502150315041505150615071508150915101511151215131514151515161517151815191520152115221523152415251526152715281529153015311532153315341535153615371538153915401541154215431544154515461547154815491550155115521553155415551556155715581559156015611562156315641565156615671568156915701571157215731574157515761577157815791580158115821583158415851586158715881589159015911592159315941595159615971598159916001601160216031604160516061607160816091610161116121613161416151616161716181619162016211622162316241625162616271628162916301631163216331634163516361637163816391640164116421643164416451646164716481649165016511652165316541655165616571658165916601661166216631664166516661667166816691670167116721673167416751676167716781679168016811682168316841685168616871688168916901691169216931694169516961697169816991700170117021703170417051706170717081709171017111712171317141715171617171718171917201721172217231724172517261727172817291730173117321733173417351736173717381739174017411742174317441745174617471748174917501751175217531754175517561757175817591760176117621763176417651766176717681769177017711772177317741775177617771778177917801781178217831784178517861787178817891790179117921793179417951796179717981799180018011802180318041805180618071808180918101811181218131814181518161817181818191820182118221823182418251826182718281829183018311832183318341835183618371838183918401841184218431844184518461847184818491850185118521853185418551856185718581859186018611862186318641865186618671868186918701871187218731874187518761877187818791880188118821883188418851886188718881889189018911892189318941895189618971898189919001901190219031904190519061907190819091910191119121913191419151916191719181919192019211922192319241925192619271928192919301931193219331934193519361937193819391940194119421943194419451946194719481949195019511952195319541955195619571958195919601961196219631964196519661967196819691970197119721973197419751976197719781979198019811982198319841985198619871988198919901991199219931994199519961997199819992000200120022003200420052006200720082009201020112012201320142015201620172018201920202021202220232024202520262027202820292030203120322033203420352036203720382039204020412042204320442045204620472048204920502051205220532054205520562057205820592060206120622063206420652066206720682069207020712072207320742075207620772078207920802081208220832084208520862087208820892090209120922093209420952096209720982099210021012102210321042105210621072108210921102111211221132114211521162117211821192120212121222123212421252126212721282129213021312132213321342135213621372138213921402141214221432144214521462147214821492150215121522153215421552156215721582159216021612162216321642165216621672168216921702171217221732174217521762177217821792180218121822183218421852186218721882189219021912192219321942195219621972198219922002201220222032204220522062207220822092210221122122213221422152216221722182219222022212222222322242225222622272228222922302231223222332234223522362237223822392240224122422243224422452246224722482249225022512252225322542255225622572258225922602261226222632264226522662267226822692270227122722273227422752276227722782279228022812282228322842285228622872288228922902291229222932294229522962297229822992300230123022303230423052306230723082309231023112312231323142315231623172318231923202321232223232324232523262327232823292330233123322333233423352336233723382339234023412342234323442345234623472348234923502351235223532354235523562357235823592360236123622363236423652366236723682369237023712372237323742375237623772378237923802381238223832384238523862387238823892390239123922393239423952396239723982399240024012402240324042405240624072408240924102411241224132414241524162417241824192420242124222423242424252426242724282429243024312432243324342435243624372438243924402441244224432444244524462447244824492450245124522453245424552456245724582459246024612462246324642465246624672468246924702471247224732474247524762477247824792480248124822483248424852486248724882489249024912492249324942495249624972498249925002501250225032504250525062507250825092510251125122513251425152516251725182519252025212522252325242525252625272528252925302531253225332534253525362537253825392540254125422543254425452546254725482549255025512552255325542555255625572558255925602561256225632564256525662567256825692570257125722573257425752576257725782579258025812582258325842585258625872588258925902591259225932594259525962597259825992600260126022603260426052606260726082609261026112612261326142615261626172618261926202621262226232624262526262627262826292630263126322633263426352636263726382639264026412642264326442645264626472648264926502651265226532654265526562657265826592660266126622663266426652666266726682669267026712672267326742675267626772678267926802681268226832684268526862687268826892690269126922693269426952696269726982699270027012702270327042705270627072708270927102711271227132714271527162717271827192720272127222723272427252726272727282729273027312732273327342735273627372738273927402741274227432744274527462747274827492750275127522753275427552756275727582759276027612762276327642765276627672768276927702771277227732774277527762777277827792780278127822783278427852786278727882789279027912792279327942795279627972798279928002801280228032804280528062807280828092810281128122813281428152816281728182819282028212822282328242825282628272828282928302831283228332834283528362837283828392840284128422843284428452846284728482849285028512852285328542855285628572858285928602861286228632864286528662867286828692870287128722873287428752876287728782879288028812882288328842885288628872888288928902891289228932894289528962897289828992900290129022903290429052906290729082909291029112912291329142915291629172918291929202921292229232924292529262927292829292930293129322933293429352936293729382939 |
- // Copyright Takatoshi Kondo 2018
- //
- // Distributed under the Boost Software License, Version 1.0.
- // (See accompanying file LICENSE_1_0.txt or copy at
- // http://www.boost.org/LICENSE_1_0.txt)
- #if !defined(MQTT_V5_MESSAGE_HPP)
- #define MQTT_V5_MESSAGE_HPP
- #include <string>
- #include <vector>
- #include <memory>
- #include <algorithm>
- #include <numeric>
- #include <boost/asio/buffer.hpp>
- #include <boost/container/static_vector.hpp>
- #include <boost/numeric/conversion/cast.hpp>
- #include <mqtt/namespace.hpp>
- #include <mqtt/two_byte_util.hpp>
- #include <mqtt/fixed_header.hpp>
- #include <mqtt/remaining_length.hpp>
- #include <mqtt/subscribe_options.hpp>
- #include <mqtt/const_buffer_util.hpp>
- #include <mqtt/will.hpp>
- #include <mqtt/connect_flags.hpp>
- #include <mqtt/publish.hpp>
- #include <mqtt/exception.hpp>
- #include <mqtt/utf8encoded_strings.hpp>
- #include <mqtt/string_check.hpp>
- #include <mqtt/property.hpp>
- #include <mqtt/property_variant.hpp>
- #include <mqtt/property_parse.hpp>
- #include <mqtt/reason_code.hpp>
- #include <mqtt/packet_id_type.hpp>
- #include <mqtt/move.hpp>
- #include <mqtt/variant_visit.hpp>
- #include <mqtt/optional.hpp>
- #if !defined(MQTT_ALWAYS_SEND_REASON_CODE)
- #define MQTT_ALWAYS_SEND_REASON_CODE false
- #endif // !defined(MQTT_ALWAYS_SEND_REASON_CODE)
- namespace MQTT_NS {
- namespace as = boost::asio;
- namespace v5 {
- namespace detail {
- class header_only_message {
- public:
- /**
- * @brief Create empty header_packet_id_message.
- */
- header_only_message(control_packet_type type, std::uint8_t flags)
- : message_ { static_cast<char>(make_fixed_header(type, flags)), 0 }
- {}
- /**
- * @brief Create const buffer sequence
- * it is for boost asio APIs
- * @return const buffer sequence
- */
- std::vector<as::const_buffer> const_buffer_sequence() const {
- return { as::buffer(message_.data(), message_.size()) };
- }
- /**
- * @brief Get whole size of sequence
- * @return whole size
- */
- std::size_t size() const {
- return message_.size();
- }
- /**
- * @brief Get number of element of const_buffer_sequence
- * @return number of element of const_buffer_sequence
- */
- static constexpr std::size_t num_of_const_buffer_sequence() {
- return 1;
- }
- /**
- * @brief Create one continuours buffer.
- * All sequence of buffers are concatinated.
- * It is useful to store to file/database.
- * @return continuous buffer
- */
- std::string continuous_buffer() const {
- return std::string(message_.data(), message_.size());
- }
- private:
- boost::container::static_vector<char, 2> message_;
- };
- } // namespace detail
- class connect_message {
- public:
- connect_message(
- std::uint16_t keep_alive_sec,
- buffer client_id,
- bool clean_start,
- optional<will> w,
- optional<buffer> user_name,
- optional<buffer> password,
- properties props
- )
- : fixed_header_(make_fixed_header(control_packet_type::connect, 0b0000)),
- connect_flags_(0),
- // protocol name length, protocol name, protocol level, connect flag, client id length, client id, keep alive
- remaining_length_(
- 2 + // protocol name length
- 4 + // protocol name
- 1 + // protocol level
- 1 + // connect flag
- 2 + // keep alive
- 2 + // client id length
- client_id.size() // client id
- ),
- protocol_name_and_level_ { 0x00, 0x04, 'M', 'Q', 'T', 'T', 0x05 },
- client_id_(force_move(client_id)),
- client_id_length_buf_{ num_to_2bytes(boost::numeric_cast<std::uint16_t>(client_id_.size())) },
- will_property_length_(
- w ?
- std::accumulate(
- w.value().props().begin(),
- w.value().props().end(),
- std::size_t(0U),
- [](std::size_t total, property_variant const& pv) {
- return total + v5::size(pv);
- }
- )
- : 0U
- ),
- will_props_(
- w ?
- force_move(w.value().props())
- : properties()
- ),
- keep_alive_buf_ ({ num_to_2bytes(keep_alive_sec ) }),
- property_length_(
- std::accumulate(
- props.begin(),
- props.end(),
- std::size_t(0U),
- [](std::size_t total, property_variant const& pv) {
- return total + v5::size(pv);
- }
- )
- ),
- props_(force_move(props)),
- num_of_const_buffer_sequence_(
- 1 + // fixed header
- 1 + // remaining length
- 1 + // protocol name and level
- 1 + // connect flags
- 1 + // keep alive
- 1 + // property length
- std::accumulate(
- props_.begin(),
- props_.end(),
- std::size_t(0U),
- [](std::size_t total, property_variant const& pv) {
- return total + v5::num_of_const_buffer_sequence(pv);
- }
- ) +
- 2 // client id length, client id
- )
- {
- auto pb = variable_bytes(property_length_);
- for (auto e : pb) {
- property_length_buf_.push_back(e);
- }
- remaining_length_ += property_length_buf_.size() + property_length_;
- utf8string_check(client_id_);
- if (clean_start) connect_flags_ |= connect_flags::clean_start;
- if (user_name) {
- utf8string_check(user_name.value());
- connect_flags_ |= connect_flags::user_name_flag;
- user_name_ = force_move(user_name.value());
- add_uint16_t_to_buf(user_name_length_buf_, boost::numeric_cast<std::uint16_t>(user_name_.size()));
- remaining_length_ += 2 + user_name_.size();
- num_of_const_buffer_sequence_ += 2; // user name length, user name
- }
- if (password) {
- connect_flags_ |= connect_flags::password_flag;
- password_ = force_move(password.value());
- add_uint16_t_to_buf(password_length_buf_, boost::numeric_cast<std::uint16_t>(password_.size()));
- remaining_length_ += 2 + password_.size();
- num_of_const_buffer_sequence_ += 2; // password length, password
- }
- if (w) {
- connect_flags_ |= connect_flags::will_flag;
- if (w.value().get_retain() == retain::yes) connect_flags_ |= connect_flags::will_retain;
- connect_flags::set_will_qos(connect_flags_, w.value().get_qos());
- auto wpb = variable_bytes(will_property_length_);
- for (auto e : wpb) {
- will_property_length_buf_.push_back(e);
- }
- utf8string_check(w.value().topic());
- will_topic_name_ = force_move(w.value().topic());
- add_uint16_t_to_buf(
- will_topic_name_length_buf_,
- boost::numeric_cast<std::uint16_t>(will_topic_name_.size())
- );
- if (w.value().message().size() > 0xffffL) throw will_message_length_error();
- will_message_ = force_move(w.value().message());
- add_uint16_t_to_buf(
- will_message_length_buf_,
- boost::numeric_cast<std::uint16_t>(will_message_.size()));
- remaining_length_ +=
- will_property_length_buf_.size() +
- will_property_length_ +
- 2 + will_topic_name_.size() + 2 + will_message_.size();
- num_of_const_buffer_sequence_ +=
- std::accumulate(
- will_props_.begin(),
- will_props_.end(),
- std::size_t(0U),
- [](std::size_t total, property_variant const& pv) {
- return total + v5::num_of_const_buffer_sequence(pv);
- }
- ) +
- 2 + // will topic name length, will topic name
- 2; // will message length, will message
- }
- auto rb = remaining_bytes(remaining_length_);
- for (auto e : rb) {
- remaining_length_buf_.push_back(e);
- }
- }
- /**
- * @brief Create const buffer sequence
- * it is for boost asio APIs
- * @return const buffer sequence
- */
- std::vector<as::const_buffer> const_buffer_sequence() const {
- std::vector<as::const_buffer> ret;
- ret.reserve(num_of_const_buffer_sequence());
- ret.emplace_back(as::buffer(&fixed_header_, 1));
- ret.emplace_back(as::buffer(remaining_length_buf_.data(), remaining_length_buf_.size()));
- ret.emplace_back(as::buffer(protocol_name_and_level_.data(), protocol_name_and_level_.size()));
- ret.emplace_back(as::buffer(&connect_flags_, 1));
- ret.emplace_back(as::buffer(keep_alive_buf_.data(), keep_alive_buf_.size()));
- ret.emplace_back(as::buffer(property_length_buf_.data(), property_length_buf_.size()));
- for (auto const& p : props_) {
- v5::add_const_buffer_sequence(ret, p);
- }
- ret.emplace_back(as::buffer(client_id_length_buf_.data(), client_id_length_buf_.size()));
- ret.emplace_back(as::buffer(client_id_));
- if (connect_flags::has_will_flag(connect_flags_)) {
- ret.emplace_back(as::buffer(will_property_length_buf_.data(), will_property_length_buf_.size()));
- for (auto const& p : will_props_) {
- v5::add_const_buffer_sequence(ret, p);
- }
- ret.emplace_back(as::buffer(will_topic_name_length_buf_.data(), will_topic_name_length_buf_.size()));
- ret.emplace_back(as::buffer(will_topic_name_));
- ret.emplace_back(as::buffer(will_message_length_buf_.data(), will_message_length_buf_.size()));
- ret.emplace_back(as::buffer(will_message_));
- }
- if (connect_flags::has_user_name_flag(connect_flags_)) {
- ret.emplace_back(as::buffer(user_name_length_buf_.data(), user_name_length_buf_.size()));
- ret.emplace_back(as::buffer(user_name_));
- }
- if (connect_flags::has_password_flag(connect_flags_)) {
- ret.emplace_back(as::buffer(password_length_buf_.data(), password_length_buf_.size()));
- ret.emplace_back(as::buffer(password_));
- }
- return ret;
- }
- /**
- * @brief Get whole size of sequence
- * @return whole size
- */
- std::size_t size() const {
- return
- 1 + // fixed header
- remaining_length_buf_.size() +
- remaining_length_;
- }
- /**
- * @brief Get number of element of const_buffer_sequence
- * @return number of element of const_buffer_sequence
- */
- constexpr std::size_t num_of_const_buffer_sequence() const {
- return num_of_const_buffer_sequence_;
- }
- /**
- * @brief Create one continuours buffer.
- * All sequence of buffers are concatinated.
- * It is useful to store to file/database.
- * @return continuous buffer
- */
- std::string continuous_buffer() const {
- std::string ret;
- ret.reserve(size());
- ret.push_back(static_cast<char>(fixed_header_));
- ret.append(remaining_length_buf_.data(), remaining_length_buf_.size());
- ret.append(protocol_name_and_level_.data(), protocol_name_and_level_.size());
- ret.push_back(connect_flags_);
- ret.append(keep_alive_buf_.data(), keep_alive_buf_.size());
- ret.append(property_length_buf_.data(), property_length_buf_.size());
- auto it = ret.end();
- ret.resize(ret.size() + property_length_);
- auto end = ret.end();
- for (auto const& p : props_) {
- v5::fill(p, it, end);
- it += static_cast<std::string::difference_type>(v5::size(p));
- }
- ret.append(client_id_length_buf_.data(), client_id_length_buf_.size());
- ret.append(client_id_.data(), client_id_.size());
- if (connect_flags::has_will_flag(connect_flags_)) {
- ret.append(will_property_length_buf_.data(), will_property_length_buf_.size());
- auto it = ret.end();
- ret.resize(ret.size() + will_property_length_);
- auto end = ret.end();
- for (auto const& p : will_props_) {
- v5::fill(p, it, end);
- it += static_cast<std::string::difference_type>(v5::size(p));
- }
- ret.append(will_topic_name_length_buf_.data(), will_topic_name_length_buf_.size());
- ret.append(will_topic_name_.data(), will_topic_name_.size());
- ret.append(will_message_length_buf_.data(), will_message_length_buf_.size());
- ret.append(will_message_.data(), will_message_.size());
- }
- if (connect_flags::has_user_name_flag(connect_flags_)) {
- ret.append(user_name_length_buf_.data(), user_name_length_buf_.size());
- ret.append(user_name_.data(), user_name_.size());
- }
- if (connect_flags::has_password_flag(connect_flags_)) {
- ret.append(password_length_buf_.data(), password_length_buf_.size());
- ret.append(password_.data(), password_.size());
- }
- return ret;
- }
- private:
- std::uint8_t fixed_header_;
- char connect_flags_;
- std::size_t remaining_length_;
- boost::container::static_vector<char, 4> remaining_length_buf_;
- boost::container::static_vector<char, 7> protocol_name_and_level_;
- buffer client_id_;
- boost::container::static_vector<char, 2> client_id_length_buf_;
- std::size_t will_property_length_;
- boost::container::static_vector<char, 4> will_property_length_buf_;
- properties will_props_;
- buffer will_topic_name_;
- boost::container::static_vector<char, 2> will_topic_name_length_buf_;
- buffer will_message_;
- boost::container::static_vector<char, 2> will_message_length_buf_;
- buffer user_name_;
- boost::container::static_vector<char, 2> user_name_length_buf_;
- buffer password_;
- boost::container::static_vector<char, 2> password_length_buf_;
- boost::container::static_vector<char, 2> keep_alive_buf_;
- std::size_t property_length_;
- boost::container::static_vector<char, 4> property_length_buf_;
- properties props_;
- std::size_t num_of_const_buffer_sequence_;
- };
- class connack_message {
- public:
- connack_message(
- bool session_present,
- connect_reason_code reason_code,
- properties props
- )
- : fixed_header_(make_fixed_header(control_packet_type::connack, 0b0000)),
- remaining_length_(
- 1 + // connect acknowledge flags
- 1 // reason code
- ),
- connect_acknowledge_flags_(session_present ? 1 : 0),
- reason_code_(reason_code),
- property_length_(
- std::accumulate(
- props.begin(),
- props.end(),
- std::size_t(0U),
- [](std::size_t total, property_variant const& pv) {
- return total + v5::size(pv);
- }
- )
- ),
- props_(force_move(props)),
- num_of_const_buffer_sequence_(
- 1 + // fixed header
- 1 + // remaining length
- 1 + // connect acknowledge flags
- 1 + // reason code
- 1 + // property length
- std::accumulate(
- props_.begin(),
- props_.end(),
- std::size_t(0U),
- [](std::size_t total, property_variant const& pv) {
- return total + v5::num_of_const_buffer_sequence(pv);
- }
- )
- )
- {
- auto pb = variable_bytes(property_length_);
- for (auto e : pb) {
- property_length_buf_.push_back(e);
- }
- remaining_length_ += property_length_buf_.size() + property_length_;
- auto rb = remaining_bytes(remaining_length_);
- for (auto e : rb) {
- remaining_length_buf_.push_back(e);
- }
- }
- /**
- * @brief Create const buffer sequence
- * it is for boost asio APIs
- * @return const buffer sequence
- */
- std::vector<as::const_buffer> const_buffer_sequence() const {
- std::vector<as::const_buffer> ret;
- ret.reserve(num_of_const_buffer_sequence());
- ret.emplace_back(as::buffer(&fixed_header_, 1));
- ret.emplace_back(as::buffer(remaining_length_buf_.data(), remaining_length_buf_.size()));
- ret.emplace_back(as::buffer(&connect_acknowledge_flags_, 1));
- ret.emplace_back(as::buffer(&reason_code_, 1));
- ret.emplace_back(as::buffer(property_length_buf_.data(), property_length_buf_.size()));
- for (auto const& p : props_) {
- v5::add_const_buffer_sequence(ret, p);
- }
- return ret;
- }
- /**
- * @brief Get whole size of sequence
- * @return whole size
- */
- std::size_t size() const {
- return
- 1 + // fixed header
- remaining_length_buf_.size() +
- remaining_length_;
- }
- /**
- * @brief Get number of element of const_buffer_sequence
- * @return number of element of const_buffer_sequence
- */
- constexpr std::size_t num_of_const_buffer_sequence() const {
- return num_of_const_buffer_sequence_;
- }
- /**
- * @brief Create one continuours buffer.
- * All sequence of buffers are concatinated.
- * It is useful to store to file/database.
- * @return continuous buffer
- */
- std::string continuous_buffer() const {
- std::string ret;
- ret.reserve(size());
- ret.push_back(static_cast<char>(fixed_header_));
- ret.append(remaining_length_buf_.data(), remaining_length_buf_.size());
- ret.push_back(static_cast<char>(connect_acknowledge_flags_));
- ret.push_back(static_cast<char>(reason_code_));
- auto it = ret.end();
- ret.resize(ret.size() + property_length_);
- auto end = ret.end();
- for (auto const& p : props_) {
- v5::fill(p, it, end);
- it += static_cast<std::string::difference_type>(v5::size(p));
- }
- return ret;
- }
- private:
- std::uint8_t fixed_header_;
- std::size_t remaining_length_;
- boost::container::static_vector<char, 4> remaining_length_buf_;
- std::uint8_t connect_acknowledge_flags_;
- connect_reason_code reason_code_;
- std::size_t property_length_;
- boost::container::static_vector<char, 4> property_length_buf_;
- properties props_;
- std::size_t num_of_const_buffer_sequence_;
- };
- template <std::size_t PacketIdBytes>
- class basic_publish_message {
- public:
- template <
- typename ConstBufferSequence,
- typename std::enable_if<
- as::is_const_buffer_sequence<ConstBufferSequence>::value,
- std::nullptr_t
- >::type = nullptr
- >
- basic_publish_message(
- typename packet_id_type<PacketIdBytes>::type packet_id,
- as::const_buffer topic_name,
- ConstBufferSequence payloads,
- publish_options pubopts,
- properties props
- )
- : fixed_header_(make_fixed_header(control_packet_type::publish, 0b0000) | pubopts.operator std::uint8_t()),
- topic_name_(topic_name),
- topic_name_length_buf_ { num_to_2bytes(boost::numeric_cast<std::uint16_t>(topic_name_.size())) },
- property_length_(
- std::accumulate(
- props.begin(),
- props.end(),
- std::size_t(0U),
- [](std::size_t total, property_variant const& pv) {
- return total + v5::size(pv);
- }
- )
- ),
- props_(force_move(props)),
- remaining_length_(
- 2 // topic name length
- + topic_name_.size() // topic name
- + ( (pubopts.get_qos() == qos::at_least_once || pubopts.get_qos() == qos::exactly_once)
- ? PacketIdBytes // packet_id
- : 0)
- ),
- num_of_const_buffer_sequence_(
- 1 + // fixed header
- 1 + // remaining length
- 1 + // topic name length
- 1 + // topic name
- ((pubopts.get_qos() == qos::at_most_once) ? 0U : 1U) + // packet id
- 1 + // property length
- std::accumulate(
- props_.begin(),
- props_.end(),
- std::size_t(0U),
- [](std::size_t total, property_variant const& pv) {
- return total + v5::num_of_const_buffer_sequence(pv);
- }
- )
- )
- {
- auto b = as::buffer_sequence_begin(payloads);
- auto e = as::buffer_sequence_end(payloads);
- auto num_of_payloads = static_cast<std::size_t>(std::distance(b, e));
- payloads_.reserve(num_of_payloads);
- for (; b != e; ++b) {
- auto const& payload = *b;
- remaining_length_ += payload.size();
- payloads_.push_back(payload);
- }
- num_of_const_buffer_sequence_ += num_of_payloads;
- utf8string_check(topic_name_);
- auto pb = variable_bytes(property_length_);
- for (auto e : pb) {
- property_length_buf_.push_back(e);
- }
- remaining_length_ += property_length_buf_.size() + property_length_;
- auto rb = remaining_bytes(remaining_length_);
- for (auto e : rb) {
- remaining_length_buf_.push_back(e);
- }
- if (pubopts.get_qos() == qos::at_least_once ||
- pubopts.get_qos() == qos::exactly_once) {
- packet_id_.reserve(PacketIdBytes);
- add_packet_id_to_buf<PacketIdBytes>::apply(packet_id_, packet_id);
- }
- }
- basic_publish_message(buffer buf) {
- if (buf.empty()) throw remaining_length_error();
- fixed_header_ = static_cast<std::uint8_t>(buf.front());
- qos qos_value = get_qos();
- buf.remove_prefix(1);
- if (buf.empty()) throw remaining_length_error();
- auto len_consumed = remaining_length(buf.begin(), buf.end());
- remaining_length_ = std::get<0>(len_consumed);
- auto consumed = std::get<1>(len_consumed);
- std::copy(
- buf.begin(),
- std::next(buf.begin(), static_cast<buffer::difference_type>(consumed)),
- std::back_inserter(remaining_length_buf_));
- buf.remove_prefix(consumed);
- if (buf.size() < 2) throw remaining_length_error();
- std::copy(buf.begin(), std::next(buf.begin(), 2), std::back_inserter(topic_name_length_buf_));
- auto topic_name_length = make_uint16_t(buf.begin(), std::next(buf.begin(), 2));
- buf.remove_prefix(2);
- if (buf.size() < topic_name_length) throw remaining_length_error();
- topic_name_ = as::buffer(buf.substr(0, topic_name_length));
- utf8string_check(topic_name_);
- buf.remove_prefix(topic_name_length);
- switch (qos_value) {
- case qos::at_most_once:
- break;
- case qos::at_least_once:
- case qos::exactly_once:
- if (buf.size() < PacketIdBytes) throw remaining_length_error();
- std::copy(buf.begin(), std::next(buf.begin(), PacketIdBytes), std::back_inserter(packet_id_));
- buf.remove_prefix(PacketIdBytes);
- break;
- default:
- throw protocol_error();
- break;
- };
- auto len_consume = variable_length(
- buf.begin(),
- buf.end()
- );
- property_length_ = std::get<0>(len_consume);
- auto consume = std::get<1>(len_consume);
- if (consume == 0) throw property_length_error();
- std::copy(
- buf.begin(),
- std::next(buf.begin(), static_cast<buffer::difference_type>(consume)),
- std::back_inserter(property_length_buf_)
- );
- buf.remove_prefix(consume);
- if (buf.size() < property_length_) throw property_length_error();
- props_ = property::parse(buf.substr(0, property_length_));
- buf.remove_prefix(property_length_);
- if (!buf.empty()) {
- payloads_.emplace_back(as::buffer(buf));
- }
- num_of_const_buffer_sequence_ =
- 1 + // fixed header
- 1 + // remaining length
- 1 + // topic name length
- 1 + // topic name
- ((qos_value == qos::at_most_once) ? 0U : 1U) + // packet id
- 1 + // property length
- std::accumulate(
- props_.begin(),
- props_.end(),
- std::size_t(0U),
- [](std::size_t total, property_variant const& pv) {
- return total + v5::num_of_const_buffer_sequence(pv);
- }
- ) +
- payloads_.size(); // payload
- }
- /**
- * @brief Create const buffer sequence
- * it is for boost asio APIs
- * @return const buffer sequence
- */
- std::vector<as::const_buffer> const_buffer_sequence() const {
- std::vector<as::const_buffer> ret;
- ret.reserve(num_of_const_buffer_sequence());
- ret.emplace_back(as::buffer(&fixed_header_, 1));
- ret.emplace_back(as::buffer(remaining_length_buf_.data(), remaining_length_buf_.size()));
- ret.emplace_back(topic_name_length_buf_.data(), topic_name_length_buf_.size());
- ret.emplace_back(as::buffer(topic_name_));
- if (!packet_id_.empty()) {
- ret.emplace_back(as::buffer(packet_id_.data(), packet_id_.size()));
- }
- ret.emplace_back(as::buffer(property_length_buf_.data(), property_length_buf_.size()));
- for (auto const& p : props_) {
- v5::add_const_buffer_sequence(ret, p);
- }
- std::copy(payloads_.begin(), payloads_.end(), std::back_inserter(ret));
- return ret;
- }
- /**
- * @brief Get whole size of sequence
- * @return whole size
- */
- std::size_t size() const {
- return
- 1 + // fixed header
- remaining_length_buf_.size() +
- remaining_length_;
- }
- /**
- * @brief Get number of element of const_buffer_sequence
- * @return number of element of const_buffer_sequence
- */
- constexpr std::size_t num_of_const_buffer_sequence() const {
- return num_of_const_buffer_sequence_;
- }
- /**
- * @brief Create one continuours buffer.
- * All sequence of buffers are concatinated.
- * It is useful to store to file/database.
- * @return continuous buffer
- */
- std::string continuous_buffer() const {
- std::string ret;
- ret.reserve(size());
- ret.push_back(static_cast<char>(fixed_header_));
- ret.append(remaining_length_buf_.data(), remaining_length_buf_.size());
- ret.append(topic_name_length_buf_.data(), topic_name_length_buf_.size());
- ret.append(get_pointer(topic_name_), get_size(topic_name_));
- ret.append(packet_id_.data(), packet_id_.size());
- ret.append(property_length_buf_.data(), property_length_buf_.size());
- auto it = ret.end();
- ret.resize(ret.size() + property_length_);
- auto end = ret.end();
- for (auto const& p : props_) {
- v5::fill(p, it, end);
- it += static_cast<std::string::difference_type>(v5::size(p));
- }
- for (auto const& payload : payloads_) {
- ret.append(get_pointer(payload), get_size(payload));
- }
- return ret;
- }
- /**
- * @brief Get packet id
- * @return packet_id
- */
- typename packet_id_type<PacketIdBytes>::type packet_id() const {
- return make_packet_id<PacketIdBytes>::apply(packet_id_.begin(), packet_id_.end());
- }
- /**
- * @brief Get publish_options
- * @return publish_options.
- */
- constexpr publish_options get_options() const {
- return publish_options(fixed_header_);
- }
- /**
- * @brief Get qos
- * @return qos
- */
- constexpr qos get_qos() const {
- return publish::get_qos(fixed_header_);
- }
- /**
- * @brief Check retain flag
- * @return true if retain, otherwise return false.
- */
- constexpr bool is_retain() const {
- return publish::is_retain(fixed_header_);
- }
- /**
- * @brief Check dup flag
- * @return true if dup, otherwise return false.
- */
- constexpr bool is_dup() const {
- return publish::is_dup(fixed_header_);
- }
- /**
- * @brief Get topic name
- * @return topic name
- */
- string_view topic() const {
- return string_view(get_pointer(topic_name_), get_size(topic_name_));
- }
- /**
- * @brief Get payload
- * @return payload
- */
- std::vector<string_view> payload() const {
- std::vector<string_view> ret;
- ret.reserve(payloads_.size());
- for (auto const& payload : payloads_) {
- ret.emplace_back(get_pointer(payload), get_size(payload));
- }
- return ret;
- }
- /**
- * @brief Get payload as single buffer
- * @return payload
- */
- buffer payload_as_buffer() const {
- auto size = std::accumulate(
- payloads_.begin(),
- payloads_.end(),
- std::size_t(0),
- [](std::size_t s, as::const_buffer const& payload) {
- return s += payload.size();
- }
- );
- if (size == 0) return buffer();
- auto spa = make_shared_ptr_array(size);
- auto ptr = spa.get();
- auto it = ptr;
- for (auto const& payload : payloads_) {
- auto b = get_pointer(payload);
- auto s = get_size(payload);
- auto e = b + s;
- std::copy(b, e, it);
- it += s;
- }
- return buffer(string_view(ptr, size), force_move(spa));
- }
- /**
- * @brief Get properties
- * @return properties
- */
- properties const& props() const {
- return props_;
- }
- /**
- * @brief Add property
- * @param p property to add
- */
- void add_prop(property_variant p) {
- auto add_size = v5::size(p);
- props_.push_back(force_move(p));
- property_length_ += add_size;
- property_length_buf_.clear();
- auto pb = variable_bytes(property_length_);
- for (auto e : pb) {
- property_length_buf_.push_back(e);
- }
- remaining_length_buf_.clear();
- remaining_length_ += add_size;
- auto rb = remaining_bytes(remaining_length_);
- for (auto e : rb) {
- remaining_length_buf_.push_back(e);
- }
- }
- /**
- * @brief Update property
- * Only fixed size property can be updated.
- * @param p property to update
- */
- template <typename Property>
- std::enable_if_t<
- std::is_base_of<property::detail::n_bytes_property<1>, Property>::value ||
- std::is_base_of<property::detail::n_bytes_property<2>, Property>::value ||
- std::is_base_of<property::detail::n_bytes_property<4>, Property>::value
- >
- update_prop(Property update_prop) {
- for (auto& p : props_) {
- MQTT_NS::visit(
- make_lambda_visitor(
- [&update_prop](Property& t) { t = std::forward<Property>(update_prop); },
- [](auto&) { }
- ),
- p
- );
- }
- }
- /**
- * @brief Remove property
- * @param id property::id to remove
- */
- void remove_prop(v5::property::id id) {
- std::size_t removed_size = 0;
- auto it = props_.begin();
- auto end = props_.begin();
- while (it != end) {
- if (v5::id(*it) == id) {
- removed_size += v5::size(*it);
- it = props_.erase(it);
- }
- else {
- ++it;
- }
- }
- property_length_ -= removed_size;
- property_length_buf_.clear();
- auto pb = variable_bytes(property_length_);
- for (auto e : pb) {
- property_length_buf_.push_back(e);
- }
- remaining_length_buf_.clear();
- remaining_length_ -= removed_size;
- auto rb = remaining_bytes(remaining_length_);
- for (auto e : rb) {
- remaining_length_buf_.push_back(e);
- }
- }
- /**
- * @brief Set dup flag
- * @param dup flag value to set
- */
- constexpr void set_dup(bool dup) {
- publish::set_dup(fixed_header_, dup);
- }
- /**
- * @brief Set topic name
- * @param topic_name value to set
- */
- void set_topic_name(as::const_buffer topic_name) {
- auto prev_topic_name_size = get_size(topic_name_);
- topic_name_ = force_move(topic_name);
- topic_name_length_buf_ = boost::container::static_vector<char, 2>{
- num_to_2bytes(boost::numeric_cast<std::uint16_t>(get_size(topic_name_)))
- };
- remaining_length_buf_.clear();
- remaining_length_ = remaining_length_ - prev_topic_name_size + get_size(topic_name_);
- auto rb = remaining_bytes(remaining_length_);
- for (auto e : rb) {
- remaining_length_buf_.push_back(e);
- }
- }
- private:
- std::uint8_t fixed_header_;
- as::const_buffer topic_name_;
- boost::container::static_vector<char, 2> topic_name_length_buf_;
- boost::container::static_vector<char, PacketIdBytes> packet_id_;
- std::size_t property_length_;
- boost::container::static_vector<char, 4> property_length_buf_;
- properties props_;
- std::vector<as::const_buffer> payloads_;
- std::size_t remaining_length_;
- boost::container::static_vector<char, 4> remaining_length_buf_;
- std::size_t num_of_const_buffer_sequence_;
- };
- using publish_message = basic_publish_message<2>;
- using publish_32_message = basic_publish_message<4>;
- template <std::size_t PacketIdBytes>
- struct basic_puback_message {
- basic_puback_message(
- typename packet_id_type<PacketIdBytes>::type packet_id,
- v5::puback_reason_code reason_code,
- properties props)
- : fixed_header_(make_fixed_header(control_packet_type::puback, 0b0000)),
- reason_code_(reason_code),
- property_length_(
- std::accumulate(
- props.begin(),
- props.end(),
- std::size_t(0U),
- [](std::size_t total, property_variant const& pv) {
- return total + v5::size(pv);
- }
- )
- ),
- props_(force_move(props)),
- num_of_const_buffer_sequence_(
- 1 + // fixed header
- 1 + // remaining length
- 1 + // packet id
- // TODO: This is wrong. The reason code MUST be provided
- // if there are properties. Not the other way around.
- // https://docs.oasis-open.org/mqtt/mqtt/v5.0/os/mqtt-v5.0-os.html#_Toc3901124
- // 3.4.2.1 PUBACK Reason Code
- // The Reason Code and Property Length can be omitted if
- // the Reason Code is 0x00 (Success) and there are no Properties.
- // In this case the PUBACK has a Remaining Length of 2.
- [&] () -> std::size_t {
- if ((reason_code_ != v5::puback_reason_code::success) || MQTT_ALWAYS_SEND_REASON_CODE) {
- // https://docs.oasis-open.org/mqtt/mqtt/v5.0/os/mqtt-v5.0-os.html#_Toc3901126
- // If the Remaining Length is less than 4 there is no Property Length and the value of 0 is used.
- if (props_.empty()) {
- return 1; // reason code
- }
- else {
- return
- 1 + // reason code
- 1 + // property length
- std::accumulate( // properties
- props_.begin(),
- props_.end(),
- std::size_t(0U),
- [](std::size_t total, property_variant const& pv) {
- return total + v5::num_of_const_buffer_sequence(pv);
- }
- );
- }
- }
- else {
- return 0;
- }
- } ()
- )
- {
- add_packet_id_to_buf<PacketIdBytes>::apply(packet_id_, packet_id);
- auto pb = variable_bytes(property_length_);
- for (auto e : pb) {
- property_length_buf_.push_back(e);
- }
- remaining_length_ =
- PacketIdBytes + // packet id
- // TODO: This is wrong. The reason code MUST be provided
- // if there are properties. Not the other way around.
- // https://docs.oasis-open.org/mqtt/mqtt/v5.0/os/mqtt-v5.0-os.html#_Toc3901124
- // 3.4.2.1 PUBACK Reason Code
- // The Reason Code and Property Length can be omitted if
- // the Reason Code is 0x00 (Success) and there are no Properties.
- // In this case the PUBACK has a Remaining Length of 2.
- [&] () -> std::size_t {
- if ((reason_code_ != v5::puback_reason_code::success) || MQTT_ALWAYS_SEND_REASON_CODE) {
- // https://docs.oasis-open.org/mqtt/mqtt/v5.0/os/mqtt-v5.0-os.html#_Toc3901126
- // If the Remaining Length is less than 4 there is no Property Length and the value of 0 is used.
- if (props_.empty()) {
- return 1; // reason code
- }
- else {
- return
- 1 + // reason code
- property_length_buf_.size() +
- property_length_;
- }
- }
- else {
- return 0;
- }
- } ();
- auto rb = remaining_bytes(remaining_length_);
- for (auto e : rb) {
- remaining_length_buf_.push_back(e);
- }
- }
- /**
- * @brief Create const buffer sequence
- * it is for boost asio APIs
- * @return const buffer sequence
- */
- std::vector<as::const_buffer> const_buffer_sequence() const {
- std::vector<as::const_buffer> ret;
- ret.reserve(num_of_const_buffer_sequence());
- ret.emplace_back(as::buffer(&fixed_header_, 1));
- ret.emplace_back(as::buffer(remaining_length_buf_.data(), remaining_length_buf_.size()));
- ret.emplace_back(as::buffer(packet_id_.data(), packet_id_.size()));
- // TODO: This is wrong. The reason code MUST be provided
- // if there are properties. Not the other way around.
- // https://docs.oasis-open.org/mqtt/mqtt/v5.0/os/mqtt-v5.0-os.html#_Toc3901124
- // 3.4.2.1 PUBACK Reason Code
- // The Reason Code and Property Length can be omitted if
- // the Reason Code is 0x00 (Success) and there are no Properties.
- // In this case the PUBACK has a Remaining Length of 2.
- if (reason_code_ != v5::puback_reason_code::success || MQTT_ALWAYS_SEND_REASON_CODE) {
- ret.emplace_back(as::buffer(&reason_code_, 1));
- // https://docs.oasis-open.org/mqtt/mqtt/v5.0/os/mqtt-v5.0-os.html#_Toc3901126
- // If the Remaining Length is less than 4 there is no Property Length and the value of 0 is used.
- if (!props_.empty()) {
- ret.emplace_back(as::buffer(property_length_buf_.data(), property_length_buf_.size()));
- for (auto const& p : props_) {
- v5::add_const_buffer_sequence(ret, p);
- }
- }
- }
- return ret;
- }
- /**
- * @brief Get whole size of sequence
- * @return whole size
- */
- std::size_t size() const {
- return
- 1 + // fixed header
- remaining_length_buf_.size() +
- remaining_length_;
- }
- /**
- * @brief Get number of element of const_buffer_sequence
- * @return number of element of const_buffer_sequence
- */
- constexpr std::size_t num_of_const_buffer_sequence() const {
- return num_of_const_buffer_sequence_;
- }
- /**
- * @brief Create one continuours buffer.
- * All sequence of buffers are concatinated.
- * It is useful to store to file/database.
- * @return continuous buffer
- */
- std::string continuous_buffer() const {
- std::string ret;
- auto sz = size();
- ret.reserve(sz);
- ret.push_back(static_cast<char>(fixed_header_));
- ret.append(remaining_length_buf_.data(), remaining_length_buf_.size());
- // TODO: This is wrong. The reason code MUST be provided
- // if there are properties. Not the other way around.
- // https://docs.oasis-open.org/mqtt/mqtt/v5.0/os/mqtt-v5.0-os.html#_Toc3901124
- // 3.4.2.1 PUBACK Reason Code
- // The Reason Code and Property Length can be omitted if
- // the Reason Code is 0x00 (Success) and there are no Properties.
- // In this case the PUBACK has a Remaining Length of 2.
- if (reason_code_ != v5::puback_reason_code::success || MQTT_ALWAYS_SEND_REASON_CODE) {
- ret.push_back(static_cast<char>(reason_code_));
- // https://docs.oasis-open.org/mqtt/mqtt/v5.0/os/mqtt-v5.0-os.html#_Toc3901126
- // If the Remaining Length is less than 4 there is no Property Length and the value of 0 is used.
- if (!props_.empty()) {
- ret.append(property_length_buf_.data(), property_length_buf_.size());
- auto it = ret.end();
- ret.resize(sz);
- auto end = ret.end();
- for (auto const& p : props_) {
- v5::fill(p, it, end);
- it += static_cast<std::string::difference_type>(v5::size(p));
- }
- }
- }
- return ret;
- }
- std::uint8_t fixed_header_;
- std::size_t remaining_length_;
- boost::container::static_vector<char, 4> remaining_length_buf_;
- boost::container::static_vector<char, PacketIdBytes> packet_id_;
- v5::puback_reason_code reason_code_;
- std::size_t property_length_;
- boost::container::static_vector<char, 4> property_length_buf_;
- properties props_;
- std::size_t num_of_const_buffer_sequence_;
- };
- using puback_message = basic_puback_message<2>;
- template <std::size_t PacketIdBytes>
- struct basic_pubrec_message {
- basic_pubrec_message(
- typename packet_id_type<PacketIdBytes>::type packet_id,
- pubrec_reason_code reason_code,
- properties props)
- : fixed_header_(make_fixed_header(control_packet_type::pubrec, 0b0000)),
- reason_code_(reason_code),
- property_length_(
- std::accumulate(
- props.begin(),
- props.end(),
- std::size_t(0U),
- [](std::size_t total, property_variant const& pv) {
- return total + v5::size(pv);
- }
- )
- ),
- props_(force_move(props)),
- num_of_const_buffer_sequence_(
- 1 + // fixed header
- 1 + // remaining length
- 1 + // packet id
- // TODO: This is wrong. The reason code MUST be provided
- // if there are properties. Not the other way around.
- // https://docs.oasis-open.org/mqtt/mqtt/v5.0/os/mqtt-v5.0-os.html#_Toc3901134
- // 3.5.2.1 PUBREC Reason Code
- // The Reason Code and Property Length can be omitted if
- // the Reason Code is 0x00 (Success) and there are no Properties.
- // In this case the PUBREC has a Remaining Length of 2.
- [&] () -> std::size_t {
- if ((reason_code_ != v5::pubrec_reason_code::success) || MQTT_ALWAYS_SEND_REASON_CODE) {
- // https://docs.oasis-open.org/mqtt/mqtt/v5.0/os/mqtt-v5.0-os.html#_Toc3901136
- // If the Remaining Length is less than 4 there is no Property Length and the value of 0 is used.
- if (props_.empty()) {
- return 1; // reason code
- }
- else {
- return
- 1 + // reason code
- 1 + // property length
- std::accumulate( // properties
- props_.begin(),
- props_.end(),
- std::size_t(0U),
- [](std::size_t total, property_variant const& pv) {
- return total + v5::num_of_const_buffer_sequence(pv);
- }
- );
- }
- }
- else {
- return 0;
- }
- } ()
- )
- {
- add_packet_id_to_buf<PacketIdBytes>::apply(packet_id_, packet_id);
- auto pb = variable_bytes(property_length_);
- for (auto e : pb) {
- property_length_buf_.push_back(e);
- }
- remaining_length_ =
- PacketIdBytes + // packet id
- // TODO: This is wrong. The reason code MUST be provided
- // if there are properties. Not the other way around.
- // https://docs.oasis-open.org/mqtt/mqtt/v5.0/os/mqtt-v5.0-os.html#_Toc3901134
- // 3.5.2.1 PUBREC Reason Code
- // The Reason Code and Property Length can be omitted if
- // the Reason Code is 0x00 (Success) and there are no Properties.
- // In this case the PUBREC has a Remaining Length of 2.
- [&] () -> std::size_t {
- if ((reason_code_ != v5::pubrec_reason_code::success) || MQTT_ALWAYS_SEND_REASON_CODE) {
- // https://docs.oasis-open.org/mqtt/mqtt/v5.0/os/mqtt-v5.0-os.html#_Toc3901136
- // If the Remaining Length is less than 4 there is no Property Length and the value of 0 is used.
- if (props_.empty()) {
- return 1; // reason code
- }
- else {
- return
- 1 + // reason code
- property_length_buf_.size() +
- property_length_;
- }
- }
- else {
- return 0;
- }
- } ();
- auto rb = remaining_bytes(remaining_length_);
- for (auto e : rb) {
- remaining_length_buf_.push_back(e);
- }
- }
- /**
- * @brief Create const buffer sequence
- * it is for boost asio APIs
- * @return const buffer sequence
- */
- std::vector<as::const_buffer> const_buffer_sequence() const {
- std::vector<as::const_buffer> ret;
- ret.reserve(num_of_const_buffer_sequence());
- ret.emplace_back(as::buffer(&fixed_header_, 1));
- ret.emplace_back(as::buffer(remaining_length_buf_.data(), remaining_length_buf_.size()));
- ret.emplace_back(as::buffer(packet_id_.data(), packet_id_.size()));
- // TODO: This is wrong. The reason code MUST be provided
- // if there are properties. Not the other way around.
- // https://docs.oasis-open.org/mqtt/mqtt/v5.0/os/mqtt-v5.0-os.html#_Toc3901134
- // 3.5.2.1 PUBREC Reason Code
- // The Reason Code and Property Length can be omitted if
- // the Reason Code is 0x00 (Success) and there are no Properties.
- // In this case the PUBREC has a Remaining Length of 2.
- if (reason_code_ != v5::pubrec_reason_code::success || MQTT_ALWAYS_SEND_REASON_CODE) {
- ret.emplace_back(as::buffer(&reason_code_, 1));
- // https://docs.oasis-open.org/mqtt/mqtt/v5.0/os/mqtt-v5.0-os.html#_Toc3901136
- // If the Remaining Length is less than 4 there is no Property Length and the value of 0 is used.
- if (!props_.empty()) {
- ret.emplace_back(as::buffer(property_length_buf_.data(), property_length_buf_.size()));
- for (auto const& p : props_) {
- v5::add_const_buffer_sequence(ret, p);
- }
- }
- }
- return ret;
- }
- /**
- * @brief Get whole size of sequence
- * @return whole size
- */
- std::size_t size() const {
- return
- 1 + // fixed header
- remaining_length_buf_.size() +
- remaining_length_;
- }
- /**
- * @brief Get number of element of const_buffer_sequence
- * @return number of element of const_buffer_sequence
- */
- constexpr std::size_t num_of_const_buffer_sequence() const {
- return num_of_const_buffer_sequence_;
- }
- /**
- * @brief Create one continuours buffer.
- * All sequence of buffers are concatinated.
- * It is useful to store to file/database.
- * @return continuous buffer
- */
- std::string continuous_buffer() const {
- std::string ret;
- auto sz = size();
- ret.reserve(sz);
- ret.push_back(static_cast<char>(fixed_header_));
- ret.append(remaining_length_buf_.data(), remaining_length_buf_.size());
- // TODO: This is wrong. The reason code MUST be provided
- // if there are properties. Not the other way around.
- // https://docs.oasis-open.org/mqtt/mqtt/v5.0/os/mqtt-v5.0-os.html#_Toc3901134
- // 3.5.2.1 PUBREC Reason Code
- // The Reason Code and Property Length can be omitted if
- // the Reason Code is 0x00 (Success) and there are no Properties.
- // In this case the PUBREC has a Remaining Length of 2.
- if (reason_code_ != v5::pubrec_reason_code::success || MQTT_ALWAYS_SEND_REASON_CODE) {
- ret.push_back(static_cast<char>(reason_code_));
- // https://docs.oasis-open.org/mqtt/mqtt/v5.0/os/mqtt-v5.0-os.html#_Toc3901136
- // If the Remaining Length is less than 4 there is no Property Length and the value of 0 is used.
- if (!props_.empty()) {
- ret.append(property_length_buf_.data(), property_length_buf_.size());
- auto it = ret.end();
- ret.resize(sz);
- auto end = ret.end();
- for (auto const& p : props_) {
- v5::fill(p, it, end);
- it += static_cast<std::string::difference_type>(v5::size(p));
- }
- }
- }
- return ret;
- }
- std::uint8_t fixed_header_;
- std::size_t remaining_length_;
- boost::container::static_vector<char, 4> remaining_length_buf_;
- boost::container::static_vector<char, PacketIdBytes> packet_id_;
- pubrec_reason_code reason_code_;
- std::size_t property_length_;
- boost::container::static_vector<char, 4> property_length_buf_;
- properties props_;
- std::size_t num_of_const_buffer_sequence_;
- };
- using pubrec_message = basic_pubrec_message<2>;
- template <std::size_t PacketIdBytes>
- struct basic_pubrel_message {
- basic_pubrel_message(
- typename packet_id_type<PacketIdBytes>::type packet_id,
- v5::pubrel_reason_code reason_code,
- properties props)
- : fixed_header_(make_fixed_header(control_packet_type::pubrel, 0b0010)),
- reason_code_(reason_code),
- property_length_(
- std::accumulate(
- props.begin(),
- props.end(),
- std::size_t(0U),
- [](std::size_t total, property_variant const& pv) {
- return total + v5::size(pv);
- }
- )
- ),
- props_(force_move(props)),
- num_of_const_buffer_sequence_(
- 1 + // fixed header
- 1 + // remaining length
- 1 + // packet id
- // TODO: This is wrong. The reason code MUST be provided
- // if there are properties. Not the other way around.
- // https://docs.oasis-open.org/mqtt/mqtt/v5.0/os/mqtt-v5.0-os.html#_Toc3901144
- // 3.6.2.1 PUBREL Reason Code
- // The Reason Code and Property Length can be omitted if
- // the Reason Code is 0x00 (Success) and there are no Properties.
- // In this case the PUBREL has a Remaining Length of 2.
- [&] () -> std::size_t {
- if ((reason_code_ != v5::pubrel_reason_code::success) || MQTT_ALWAYS_SEND_REASON_CODE) {
- // https://docs.oasis-open.org/mqtt/mqtt/v5.0/os/mqtt-v5.0-os.html#_Toc3901146
- // If the Remaining Length is less than 4 there is no Property Length and the value of 0 is used.
- if (props_.empty()) {
- return 1; // reason code
- }
- else {
- return
- 1 + // reason code
- 1 + // property length
- std::accumulate( // properties
- props_.begin(),
- props_.end(),
- std::size_t(0U),
- [](std::size_t total, property_variant const& pv) {
- return total + v5::num_of_const_buffer_sequence(pv);
- }
- );
- }
- }
- else {
- return 0;
- }
- } ()
- )
- {
- add_packet_id_to_buf<PacketIdBytes>::apply(packet_id_, packet_id);
- auto pb = variable_bytes(property_length_);
- for (auto e : pb) {
- property_length_buf_.push_back(e);
- }
- remaining_length_ =
- PacketIdBytes + // packet id
- // TODO: This is wrong. The reason code MUST be provided
- // if there are properties. Not the other way around.
- // https://docs.oasis-open.org/mqtt/mqtt/v5.0/os/mqtt-v5.0-os.html#_Toc3901144
- // 3.6.2.1 PUBREL Reason Code
- // The Reason Code and Property Length can be omitted if
- // the Reason Code is 0x00 (Success) and there are no Properties.
- // In this case the PUBREL has a Remaining Length of 2.
- [&] () -> std::size_t {
- if ((reason_code_ != v5::pubrel_reason_code::success) || MQTT_ALWAYS_SEND_REASON_CODE) {
- // https://docs.oasis-open.org/mqtt/mqtt/v5.0/os/mqtt-v5.0-os.html#_Toc3901146
- // If the Remaining Length is less than 4 there is no Property Length and the value of 0 is used.
- if (props_.empty()) {
- return 1; // reason code
- }
- else {
- return
- 1 + // reason code
- property_length_buf_.size() +
- property_length_;
- }
- }
- else {
- return 0;
- }
- } ();
- auto rb = remaining_bytes(remaining_length_);
- for (auto e : rb) {
- remaining_length_buf_.push_back(e);
- }
- }
- basic_pubrel_message(buffer buf) {
- if (buf.empty()) throw remaining_length_error();
- fixed_header_ = static_cast<std::uint8_t>(buf.front());
- buf.remove_prefix(1);
- if (buf.empty()) throw remaining_length_error();
- auto len_consumed = remaining_length(buf.begin(), buf.end());
- remaining_length_ = std::get<0>(len_consumed);
- auto consumed = std::get<1>(len_consumed);
- std::copy(
- buf.begin(),
- std::next(buf.begin(), static_cast<buffer::difference_type>(consumed)),
- std::back_inserter(remaining_length_buf_));
- buf.remove_prefix(consumed);
- if (buf.size() < PacketIdBytes) throw remaining_length_error();
- std::copy(buf.begin(), std::next(buf.begin(), PacketIdBytes), std::back_inserter(packet_id_));
- buf.remove_prefix(PacketIdBytes);
- if (buf.empty()) {
- num_of_const_buffer_sequence_ =
- 1 + // fixed header
- 1 + // remaining length
- 1; // packet id
- reason_code_ = v5::pubrel_reason_code::success;
- return;
- }
- reason_code_ = static_cast<v5::pubrel_reason_code>(buf.front());
- buf.remove_prefix(1);
- if (buf.empty()) {
- property_length_ = 0;
- }
- else {
- auto len_consume = variable_length(
- buf.begin(),
- buf.end()
- );
- property_length_ = std::get<0>(len_consume);
- auto consume = std::get<1>(len_consume);
- if (consume == 0) throw property_length_error();
- std::copy(
- buf.begin(),
- std::next(buf.begin(), static_cast<buffer::difference_type>(consume)),
- std::back_inserter(property_length_buf_)
- );
- buf.remove_prefix(consume);
- if (buf.size() != property_length_) throw property_length_error();
- props_ = property::parse(buf);
- buf.remove_prefix(property_length_);
- }
- num_of_const_buffer_sequence_ =
- 1 + // fixed header
- 1 + // remaining length
- 1 + // packet id
- // TODO: This is wrong. The reason code MUST be provided
- // if there are properties. Not the other way around.
- // https://docs.oasis-open.org/mqtt/mqtt/v5.0/os/mqtt-v5.0-os.html#_Toc3901144
- // 3.6.2.1 PUBREL Reason Code
- // The Reason Code and Property Length can be omitted if
- // the Reason Code is 0x00 (Success) and there are no Properties.
- // In this case the PUBREL has a Remaining Length of 2.
- [&] () -> std::size_t {
- if ((reason_code_ != v5::pubrel_reason_code::success) || MQTT_ALWAYS_SEND_REASON_CODE) {
- // https://docs.oasis-open.org/mqtt/mqtt/v5.0/os/mqtt-v5.0-os.html#_Toc3901146
- // If the Remaining Length is less than 4 there is no Property Length and the value of 0 is used.
- if (props_.empty()) {
- return 1; // reason code
- }
- else {
- return
- 1 + // reason code
- 1 + // property length
- std::accumulate( // properties
- props_.begin(),
- props_.end(),
- std::size_t(0U),
- [](std::size_t total, property_variant const& pv) {
- return total + v5::num_of_const_buffer_sequence(pv);
- }
- );
- }
- }
- else {
- return 0;
- }
- } ();
- }
- /**
- * @brief Create const buffer sequence
- * it is for boost asio APIs
- * @return const buffer sequence
- */
- std::vector<as::const_buffer> const_buffer_sequence() const {
- std::vector<as::const_buffer> ret;
- ret.reserve(num_of_const_buffer_sequence());
- ret.emplace_back(as::buffer(&fixed_header_, 1));
- ret.emplace_back(as::buffer(remaining_length_buf_.data(), remaining_length_buf_.size()));
- ret.emplace_back(as::buffer(packet_id_.data(), packet_id_.size()));
- // TODO: This is wrong. The reason code MUST be provided
- // if there are properties. Not the other way around.
- // https://docs.oasis-open.org/mqtt/mqtt/v5.0/os/mqtt-v5.0-os.html#_Toc3901144
- // 3.6.2.1 PUBREL Reason Code
- // The Reason Code and Property Length can be omitted if
- // the Reason Code is 0x00 (Success) and there are no Properties.
- // In this case the PUBREL has a Remaining Length of 2.
- if(reason_code_ != v5::pubrel_reason_code::success || MQTT_ALWAYS_SEND_REASON_CODE) {
- ret.emplace_back(as::buffer(&reason_code_, 1));
- // https://docs.oasis-open.org/mqtt/mqtt/v5.0/os/mqtt-v5.0-os.html#_Toc3901146
- // If the Remaining Length is less than 4 there is no Property Length and the value of 0 is used.
- if (!props_.empty()) {
- ret.emplace_back(as::buffer(property_length_buf_.data(), property_length_buf_.size()));
- for (auto const& p : props_) {
- v5::add_const_buffer_sequence(ret, p);
- }
- }
- }
- return ret;
- }
- /**
- * @brief Get whole size of sequence
- * @return whole size
- */
- std::size_t size() const {
- return
- 1 + // fixed header
- remaining_length_buf_.size() +
- remaining_length_;
- }
- /**
- * @brief Get number of element of const_buffer_sequence
- * @return number of element of const_buffer_sequence
- */
- constexpr std::size_t num_of_const_buffer_sequence() const {
- return num_of_const_buffer_sequence_;
- }
- /**
- * @brief Create one continuours buffer.
- * All sequence of buffers are concatinated.
- * It is useful to store to file/database.
- * @return continuous buffer
- */
- std::string continuous_buffer() const {
- std::string ret;
- auto sz = size();
- ret.reserve(sz);
- ret.push_back(static_cast<char>(fixed_header_));
- ret.append(remaining_length_buf_.data(), remaining_length_buf_.size());
- ret.append(packet_id_.data(), packet_id_.size());
- // TODO: This is wrong. The reason code MUST be provided
- // if there are properties. Not the other way around.
- // https://docs.oasis-open.org/mqtt/mqtt/v5.0/os/mqtt-v5.0-os.html#_Toc3901144
- // 3.6.2.1 PUBREL Reason Code
- // The Reason Code and Property Length can be omitted if
- // the Reason Code is 0x00 (Success) and there are no Properties.
- // In this case the PUBREL has a Remaining Length of 2.
- if (reason_code_ != v5::pubrel_reason_code::success || MQTT_ALWAYS_SEND_REASON_CODE) {
- ret.push_back(static_cast<char>(reason_code_));
- // https://docs.oasis-open.org/mqtt/mqtt/v5.0/os/mqtt-v5.0-os.html#_Toc3901146
- // If the Remaining Length is less than 4 there is no Property Length and the value of 0 is used.
- if (!props_.empty()) {
- ret.append(property_length_buf_.data(), property_length_buf_.size());
- auto it = ret.end();
- ret.resize(sz);
- auto end = ret.end();
- for (auto const& p : props_) {
- v5::fill(p, it, end);
- it += static_cast<std::string::difference_type>(v5::size(p));
- }
- }
- }
- return ret;
- }
- /**
- * @brief Get packet id
- * @return packet_id
- */
- decltype(auto) packet_id() const {
- return make_packet_id<PacketIdBytes>::apply(packet_id_.begin(), packet_id_.end());
- }
- /**
- * @brief Get reason_code
- * @return reason_code
- */
- v5::pubrel_reason_code reason_code() const {
- return reason_code_;
- }
- /**
- * @brief Get properties
- * @return properties
- */
- properties const& props() const {
- return props_;
- }
- std::uint8_t fixed_header_;
- std::size_t remaining_length_;
- boost::container::static_vector<char, 4> remaining_length_buf_;
- boost::container::static_vector<char, PacketIdBytes> packet_id_;
- v5::pubrel_reason_code reason_code_;
- std::size_t property_length_;
- boost::container::static_vector<char, 4> property_length_buf_;
- properties props_;
- std::size_t num_of_const_buffer_sequence_;
- };
- using pubrel_message = basic_pubrel_message<2>;
- using pubrel_32_message = basic_pubrel_message<4>;
- template <std::size_t PacketIdBytes>
- struct basic_pubcomp_message {
- basic_pubcomp_message(
- typename packet_id_type<PacketIdBytes>::type packet_id,
- pubcomp_reason_code reason_code,
- properties props)
- : fixed_header_(make_fixed_header(control_packet_type::pubcomp, 0b0000)),
- reason_code_(reason_code),
- property_length_(
- std::accumulate(
- props.begin(),
- props.end(),
- std::size_t(0U),
- [](std::size_t total, property_variant const& pv) {
- return total + v5::size(pv);
- }
- )
- ),
- props_(force_move(props)),
- num_of_const_buffer_sequence_(
- 1 + // fixed header
- 1 + // remaining length
- 1 + // packet id
- // TODO: This is wrong. The reason code MUST be provided
- // if there are properties. Not the other way around.
- // https://docs.oasis-open.org/mqtt/mqtt/v5.0/os/mqtt-v5.0-os.html#_Toc3901154
- // 3.7.2.1 PUBCOMP Reason Code
- // The Reason Code and Property Length can be omitted if
- // the Reason Code is 0x00 (Success) and there are no Properties.
- // In this case the PUBCOMP has a Remaining Length of 2.
- [&] () -> std::size_t {
- if ((reason_code_ != v5::pubcomp_reason_code::success) || MQTT_ALWAYS_SEND_REASON_CODE) {
- // https://docs.oasis-open.org/mqtt/mqtt/v5.0/os/mqtt-v5.0-os.html#_Toc3901156
- // If the Remaining Length is less than 4 there is no Property Length and the value of 0 is used.
- if (props_.empty()) {
- return 1; // reason code
- }
- else {
- return
- 1 + // reason code
- 1 + // property length
- std::accumulate( // properties
- props_.begin(),
- props_.end(),
- std::size_t(0U),
- [](std::size_t total, property_variant const& pv) {
- return total + v5::num_of_const_buffer_sequence(pv);
- }
- );
- }
- }
- else {
- return 0;
- }
- } ()
- )
- {
- add_packet_id_to_buf<PacketIdBytes>::apply(packet_id_, packet_id);
- auto pb = variable_bytes(property_length_);
- for (auto e : pb) {
- property_length_buf_.push_back(e);
- }
- remaining_length_ =
- PacketIdBytes + // packet id
- // TODO: This is wrong. The reason code MUST be provided
- // if there are properties. Not the other way around.
- // https://docs.oasis-open.org/mqtt/mqtt/v5.0/os/mqtt-v5.0-os.html#_Toc3901154
- // 3.7.2.1 PUBCOMP Reason Code
- // The Reason Code and Property Length can be omitted if
- // the Reason Code is 0x00 (Success) and there are no Properties.
- // In this case the PUBCOMP has a Remaining Length of 2.
- [&] () -> std::size_t {
- if ((reason_code_ != v5::pubcomp_reason_code::success) || MQTT_ALWAYS_SEND_REASON_CODE) {
- // https://docs.oasis-open.org/mqtt/mqtt/v5.0/os/mqtt-v5.0-os.html#_Toc3901156
- // If the Remaining Length is less than 4 there is no Property Length and the value of 0 is used.
- if (props_.empty()) {
- return 1; // reason code
- }
- else {
- return
- 1 + // reason code
- property_length_buf_.size() +
- property_length_;
- }
- }
- else {
- return 0;
- }
- } ();
- auto rb = remaining_bytes(remaining_length_);
- for (auto e : rb) {
- remaining_length_buf_.push_back(e);
- }
- }
- /**
- * @brief Create const buffer sequence
- * it is for boost asio APIs
- * @return const buffer sequence
- */
- std::vector<as::const_buffer> const_buffer_sequence() const {
- std::vector<as::const_buffer> ret;
- ret.reserve(num_of_const_buffer_sequence());
- ret.emplace_back(as::buffer(&fixed_header_, 1));
- ret.emplace_back(as::buffer(remaining_length_buf_.data(), remaining_length_buf_.size()));
- ret.emplace_back(as::buffer(packet_id_.data(), packet_id_.size()));
- // TODO: This is wrong. The reason code MUST be provided
- // if there are properties. Not the other way around.
- // https://docs.oasis-open.org/mqtt/mqtt/v5.0/os/mqtt-v5.0-os.html#_Toc3901154
- // 3.7.2.1 PUBCOMP Reason Code
- // The Reason Code and Property Length can be omitted if
- // the Reason Code is 0x00 (Success) and there are no Properties.
- // In this case the PUBCOMP has a Remaining Length of 2.
- if (reason_code_ != v5::pubcomp_reason_code::success || MQTT_ALWAYS_SEND_REASON_CODE) {
- ret.emplace_back(as::buffer(&reason_code_, 1));
- // https://docs.oasis-open.org/mqtt/mqtt/v5.0/os/mqtt-v5.0-os.html#_Toc3901156
- // If the Remaining Length is less than 4 there is no Property Length and the value of 0 is used.
- if (!props_.empty()) {
- ret.emplace_back(as::buffer(property_length_buf_.data(), property_length_buf_.size()));
- for (auto const& p : props_) {
- v5::add_const_buffer_sequence(ret, p);
- }
- }
- }
- return ret;
- }
- /**
- * @brief Get whole size of sequence
- * @return whole size
- */
- std::size_t size() const {
- return
- 1 + // fixed header
- remaining_length_buf_.size() +
- remaining_length_;
- }
- /**
- * @brief Get number of element of const_buffer_sequence
- * @return number of element of const_buffer_sequence
- */
- constexpr std::size_t num_of_const_buffer_sequence() const {
- return num_of_const_buffer_sequence_;
- }
- /**
- * @brief Create one continuours buffer.
- * All sequence of buffers are concatinated.
- * It is useful to store to file/database.
- * @return continuous buffer
- */
- std::string continuous_buffer() const {
- std::string ret;
- auto sz = size();
- ret.reserve(sz);
- ret.push_back(static_cast<char>(fixed_header_));
- ret.append(remaining_length_buf_.data(), remaining_length_buf_.size());
- // TODO: This is wrong. The reason code MUST be provided
- // if there are properties. Not the other way around.
- // https://docs.oasis-open.org/mqtt/mqtt/v5.0/os/mqtt-v5.0-os.html#_Toc3901154
- // 3.7.2.1 PUBCOMP Reason Code
- // The Reason Code and Property Length can be omitted if
- // the Reason Code is 0x00 (Success) and there are no Properties.
- // In this case the PUBCOMP has a Remaining Length of 2.
- if (reason_code_ != v5::pubcomp_reason_code::success || MQTT_ALWAYS_SEND_REASON_CODE) {
- ret.push_back(static_cast<char>(reason_code_));
- // https://docs.oasis-open.org/mqtt/mqtt/v5.0/os/mqtt-v5.0-os.html#_Toc3901156
- // If the Remaining Length is less than 4 there is no Property Length and the value of 0 is used.
- if (!props_.empty()) {
- ret.append(property_length_buf_.data(), property_length_buf_.size());
- auto it = ret.end();
- ret.resize(sz);
- auto end = ret.end();
- for (auto const& p : props_) {
- v5::fill(p, it, end);
- it += static_cast<std::string::difference_type>(v5::size(p));
- }
- }
- }
- return ret;
- }
- std::uint8_t fixed_header_;
- std::size_t remaining_length_;
- boost::container::static_vector<char, 4> remaining_length_buf_;
- boost::container::static_vector<char, PacketIdBytes> packet_id_;
- pubcomp_reason_code reason_code_;
- std::size_t property_length_;
- boost::container::static_vector<char, 4> property_length_buf_;
- properties props_;
- std::size_t num_of_const_buffer_sequence_;
- };
- using pubcomp_message = basic_pubcomp_message<2>;
- template <std::size_t PacketIdBytes>
- class basic_subscribe_message {
- private:
- struct entry {
- entry(as::const_buffer topic_filter, subscribe_options options)
- : topic_filter_(topic_filter),
- topic_filter_length_buf_ { num_to_2bytes(boost::numeric_cast<std::uint16_t>(topic_filter_.size())) },
- options_(options)
- {}
- as::const_buffer topic_filter_;
- boost::container::static_vector<char, 2> topic_filter_length_buf_;
- subscribe_options options_;
- };
- public:
- basic_subscribe_message(
- std::vector<std::tuple<as::const_buffer, subscribe_options>> params,
- typename packet_id_type<PacketIdBytes>::type packet_id,
- properties props
- )
- : fixed_header_(make_fixed_header(control_packet_type::subscribe, 0b0010)),
- remaining_length_(PacketIdBytes),
- property_length_(
- std::accumulate(
- props.begin(),
- props.end(),
- std::size_t(0U),
- [](std::size_t total, property_variant const& pv) {
- return total + v5::size(pv);
- }
- )
- ),
- props_(force_move(props)),
- num_of_const_buffer_sequence_(
- 1 + // fixed header
- 1 + // remaining length
- 1 + // packet id
- 1 + // property length
- std::accumulate(
- props_.begin(),
- props_.end(),
- std::size_t(0U),
- [](std::size_t total, property_variant const& pv) {
- return total + v5::num_of_const_buffer_sequence(pv);
- }
- ) +
- params.size() * 3 // topic filter length, topic filter, qos
- )
- {
- add_packet_id_to_buf<PacketIdBytes>::apply(packet_id_, packet_id);
- auto pb = variable_bytes(property_length_);
- for (auto e : pb) {
- property_length_buf_.push_back(e);
- }
- remaining_length_ +=
- property_length_buf_.size() +
- property_length_;
- // Check for errors before allocating.
- for (auto&& e : params) {
- as::const_buffer topic_filter = std::get<0>(e);
- utf8string_check(topic_filter);
- }
- entries_.reserve(params.size());
- for (auto&& e : params) {
- as::const_buffer topic_filter = std::get<0>(e);
- size_t size = topic_filter.size();
- entries_.emplace_back(topic_filter, std::get<1>(e));
- remaining_length_ +=
- 2 + // topic filter length
- size + // topic filter
- 1; // means QoS
- }
- auto rb = remaining_bytes(remaining_length_);
- for (auto e : rb) {
- remaining_length_buf_.push_back(e);
- }
- }
- /**
- * @brief Create const buffer sequence
- * it is for boost asio APIs
- * @return const buffer sequence
- */
- std::vector<as::const_buffer> const_buffer_sequence() const {
- std::vector<as::const_buffer> ret;
- ret.reserve(num_of_const_buffer_sequence());
- ret.emplace_back(as::buffer(&fixed_header_, 1));
- ret.emplace_back(as::buffer(remaining_length_buf_.data(), remaining_length_buf_.size()));
- ret.emplace_back(as::buffer(packet_id_.data(), packet_id_.size()));
- ret.emplace_back(as::buffer(property_length_buf_.data(), property_length_buf_.size()));
- for (auto const& p : props_) {
- v5::add_const_buffer_sequence(ret, p);
- }
- for (auto const& e : entries_) {
- ret.emplace_back(as::buffer(e.topic_filter_length_buf_.data(), e.topic_filter_length_buf_.size()));
- ret.emplace_back(as::buffer(e.topic_filter_));
- ret.emplace_back(as::buffer(&e.options_, 1));
- }
- return ret;
- }
- /**
- * @brief Get whole size of sequence
- * @return whole size
- */
- std::size_t size() const {
- return
- 1 + // fixed header
- remaining_length_buf_.size() +
- remaining_length_;
- }
- /**
- * @brief Get number of element of const_buffer_sequence
- * @return number of element of const_buffer_sequence
- */
- constexpr std::size_t num_of_const_buffer_sequence() const {
- return num_of_const_buffer_sequence_;
- }
- /**
- * @brief Create one continuours buffer.
- * All sequence of buffers are concatinated.
- * It is useful to store to file/database.
- * @return continuous buffer
- */
- std::string continuous_buffer() const {
- std::string ret;
- ret.reserve(size());
- ret.push_back(static_cast<char>(fixed_header_));
- ret.append(remaining_length_buf_.data(), remaining_length_buf_.size());
- ret.append(packet_id_.data(), packet_id_.size());
- ret.append(property_length_buf_.data(), property_length_buf_.size());
- auto it = ret.end();
- ret.resize(ret.size() + property_length_);
- auto end = ret.end();
- for (auto const& p : props_) {
- v5::fill(p, it, end);
- it += static_cast<std::string::difference_type>(v5::size(p));
- }
- for (auto const& e : entries_) {
- ret.append(e.topic_filter_length_buf_.data(), e.topic_filter_length_buf_.size());
- ret.append(get_pointer(e.topic_filter_), get_size(e.topic_filter_));
- ret.push_back(static_cast<char>(e.options_.operator std::uint8_t()));
- }
- return ret;
- }
- private:
- std::uint8_t fixed_header_;
- std::vector<entry> entries_;
- boost::container::static_vector<char, PacketIdBytes> packet_id_;
- std::size_t remaining_length_;
- boost::container::static_vector<char, 4> remaining_length_buf_;
- std::size_t property_length_;
- boost::container::static_vector<char, 4> property_length_buf_;
- properties props_;
- std::size_t num_of_const_buffer_sequence_;
- };
- using subscribe_message = basic_subscribe_message<2>;
- template <std::size_t PacketIdBytes>
- class basic_suback_message {
- public:
- basic_suback_message(
- std::vector<suback_reason_code> reason_codes,
- typename packet_id_type<PacketIdBytes>::type packet_id,
- properties props
- )
- : fixed_header_(make_fixed_header(control_packet_type::suback, 0b0000)),
- remaining_length_(reason_codes.size() + PacketIdBytes),
- property_length_(
- std::accumulate(
- props.begin(),
- props.end(),
- std::size_t(0U),
- [](std::size_t total, property_variant const& pv) {
- return total + v5::size(pv);
- }
- )
- ),
- props_(force_move(props)),
- num_of_const_buffer_sequence_(
- 1 + // fixed header
- 1 + // remaining length
- 1 + // packet id
- 1 + // property length
- std::accumulate(
- props_.begin(),
- props_.end(),
- std::size_t(0U),
- [](std::size_t total, property_variant const& pv) {
- return total + v5::num_of_const_buffer_sequence(pv);
- }
- ) +
- 1 // entries (reason code ...)
- )
- {
- add_packet_id_to_buf<PacketIdBytes>::apply(packet_id_, packet_id);
- auto pb = variable_bytes(property_length_);
- for (auto e : pb) {
- property_length_buf_.push_back(e);
- }
- remaining_length_ +=
- property_length_buf_.size() +
- property_length_;
- auto rb = remaining_bytes(remaining_length_);
- for (auto e : rb) {
- remaining_length_buf_.push_back(e);
- }
- entries_.reserve(reason_codes.size());
- for (auto e : reason_codes) {
- entries_.push_back(static_cast<char>(e));
- }
- }
- /**
- * @brief Create const buffer sequence
- * it is for boost asio APIs
- * @return const buffer sequence
- */
- std::vector<as::const_buffer> const_buffer_sequence() const {
- std::vector<as::const_buffer> ret;
- ret.reserve(num_of_const_buffer_sequence());
- ret.emplace_back(as::buffer(&fixed_header_, 1));
- ret.emplace_back(as::buffer(remaining_length_buf_.data(), remaining_length_buf_.size()));
- ret.emplace_back(as::buffer(packet_id_.data(), packet_id_.size()));
- ret.emplace_back(as::buffer(property_length_buf_.data(), property_length_buf_.size()));
- for (auto const& p : props_) {
- v5::add_const_buffer_sequence(ret, p);
- }
- ret.emplace_back(as::buffer(entries_));
- return ret;
- }
- /**
- * @brief Get whole size of sequence
- * @return whole size
- */
- std::size_t size() const {
- return
- 1 + // fixed header
- remaining_length_buf_.size() +
- remaining_length_;
- }
- /**
- * @brief Get number of element of const_buffer_sequence
- * @return number of element of const_buffer_sequence
- */
- constexpr std::size_t num_of_const_buffer_sequence() const {
- return num_of_const_buffer_sequence_;
- }
- /**
- * @brief Create one continuours buffer.
- * All sequence of buffers are concatinated.
- * It is useful to store to file/database.
- * @return continuous buffer
- */
- std::string continuous_buffer() const {
- std::string ret;
- ret.reserve(size());
- ret.push_back(static_cast<char>(fixed_header_));
- ret.append(remaining_length_buf_.data(), remaining_length_buf_.size());
- ret.append(packet_id_.data(), packet_id_.size());
- auto it = ret.end();
- ret.resize(ret.size() + property_length_);
- auto end = ret.end();
- for (auto const& p : props_) {
- v5::fill(p, it, end);
- it += static_cast<std::string::difference_type>(v5::size(p));
- }
- ret.append(entries_);
- return ret;
- }
- private:
- std::uint8_t fixed_header_;
- std::string entries_;
- boost::container::static_vector<char, PacketIdBytes> packet_id_;
- std::size_t remaining_length_;
- boost::container::static_vector<char, 4> remaining_length_buf_;
- std::size_t property_length_;
- boost::container::static_vector<char, 4> property_length_buf_;
- properties props_;
- std::size_t num_of_const_buffer_sequence_;
- };
- using suback_message = basic_suback_message<2>;
- template <std::size_t PacketIdBytes>
- class basic_unsubscribe_message {
- private:
- struct entry {
- entry(as::const_buffer topic_filter)
- : topic_filter_(topic_filter),
- topic_filter_length_buf_ { num_to_2bytes(boost::numeric_cast<std::uint16_t>(topic_filter.size())) }
- {}
- as::const_buffer topic_filter_;
- boost::container::static_vector<char, 2> topic_filter_length_buf_;
- };
- public:
- basic_unsubscribe_message(
- std::vector<as::const_buffer> params,
- typename packet_id_type<PacketIdBytes>::type packet_id,
- properties props
- )
- : fixed_header_(make_fixed_header(control_packet_type::unsubscribe, 0b0010)),
- remaining_length_(PacketIdBytes),
- property_length_(
- std::accumulate(
- props.begin(),
- props.end(),
- std::size_t(0U),
- [](std::size_t total, property_variant const& pv) {
- return total + v5::size(pv);
- }
- )
- ),
- props_(force_move(props)),
- num_of_const_buffer_sequence_(
- 1 + // fixed header
- 1 + // remaining length
- 1 + // packet id
- 1 + // property length
- std::accumulate(
- props_.begin(),
- props_.end(),
- std::size_t(0U),
- [](std::size_t total, property_variant const& pv) {
- return total + v5::num_of_const_buffer_sequence(pv);
- }
- ) +
- params.size() * 2 // topic filter length, topic filter
- )
- {
- add_packet_id_to_buf<PacketIdBytes>::apply(packet_id_, packet_id);
- auto pb = variable_bytes(property_length_);
- for (auto e : pb) {
- property_length_buf_.push_back(e);
- }
- remaining_length_ +=
- property_length_buf_.size() +
- property_length_;
- // Check for errors before allocating.
- for (auto&& e : params) {
- utf8string_check(e);
- }
- entries_.reserve(params.size());
- for (auto&& e : params) {
- auto size = e.size();
- entries_.emplace_back(e);
- remaining_length_ +=
- 2 + // topic filter length
- size; // topic filter
- }
- auto rb = remaining_bytes(remaining_length_);
- for (auto e : rb) {
- remaining_length_buf_.push_back(e);
- }
- }
- /**
- * @brief Create const buffer sequence
- * it is for boost asio APIs
- * @return const buffer sequence
- */
- std::vector<as::const_buffer> const_buffer_sequence() const {
- std::vector<as::const_buffer> ret;
- ret.reserve(num_of_const_buffer_sequence());
- ret.emplace_back(as::buffer(&fixed_header_, 1));
- ret.emplace_back(as::buffer(remaining_length_buf_.data(), remaining_length_buf_.size()));
- ret.emplace_back(as::buffer(packet_id_.data(), packet_id_.size()));
- ret.emplace_back(as::buffer(property_length_buf_.data(), property_length_buf_.size()));
- for (auto const& p : props_) {
- v5::add_const_buffer_sequence(ret, p);
- }
- for (auto const& e : entries_) {
- ret.emplace_back(as::buffer(e.topic_filter_length_buf_.data(), e.topic_filter_length_buf_.size()));
- ret.emplace_back(as::buffer(e.topic_filter_));
- }
- return ret;
- }
- /**
- * @brief Get whole size of sequence
- * @return whole size
- */
- std::size_t size() const {
- return
- 1 + // fixed header
- remaining_length_buf_.size() +
- remaining_length_;
- }
- /**
- * @brief Get number of element of const_buffer_sequence
- * @return number of element of const_buffer_sequence
- */
- constexpr std::size_t num_of_const_buffer_sequence() const {
- return num_of_const_buffer_sequence_;
- }
- /**
- * @brief Create one continuours buffer.
- * All sequence of buffers are concatinated.
- * It is useful to store to file/database.
- * @return continuous buffer
- */
- std::string continuous_buffer() const {
- std::string ret;
- ret.reserve(size());
- ret.push_back(static_cast<char>(fixed_header_));
- ret.append(remaining_length_buf_.data(), remaining_length_buf_.size());
- ret.append(packet_id_.data(), packet_id_.size());
- auto it = ret.end();
- ret.resize(ret.size() + property_length_);
- auto end = ret.end();
- for (auto const& p : props_) {
- v5::fill(p, it, end);
- it += static_cast<std::string::difference_type>(v5::size(p));
- }
- for (auto const& e : entries_) {
- ret.append(e.topic_filter_length_buf_.data(), e.topic_filter_length_buf_.size());
- ret.append(get_pointer(e.topic_filter_), get_size(e.topic_filter_));
- }
- return ret;
- }
- private:
- std::uint8_t fixed_header_;
- std::vector<entry> entries_;
- boost::container::static_vector<char, PacketIdBytes> packet_id_;
- std::size_t remaining_length_;
- boost::container::static_vector<char, 4> remaining_length_buf_;
- std::size_t property_length_;
- boost::container::static_vector<char, 4> property_length_buf_;
- properties props_;
- std::size_t num_of_const_buffer_sequence_;
- };
- using unsubscribe_message = basic_unsubscribe_message<2>;
- template <std::size_t PacketIdBytes>
- class basic_unsuback_message {
- public:
- basic_unsuback_message(
- std::vector<v5::unsuback_reason_code> reason_codes,
- typename packet_id_type<PacketIdBytes>::type packet_id,
- properties props
- )
- : fixed_header_(make_fixed_header(control_packet_type::unsuback, 0b0000)),
- reason_codes_(force_move(reason_codes)),
- remaining_length_(reason_codes_.size() + PacketIdBytes),
- property_length_(
- std::accumulate(
- props.begin(),
- props.end(),
- std::size_t(0U),
- [](std::size_t total, property_variant const& pv) {
- return total + v5::size(pv);
- }
- )
- ),
- props_(force_move(props)),
- num_of_const_buffer_sequence_(
- 1 + // fixed header
- 1 + // remaining length
- 1 + // packet id
- 1 + // property length
- std::accumulate(
- props_.begin(),
- props_.end(),
- std::size_t(0U),
- [](std::size_t total, property_variant const& pv) {
- return total + v5::num_of_const_buffer_sequence(pv);
- }
- )
- )
- {
- add_packet_id_to_buf<PacketIdBytes>::apply(packet_id_, packet_id);
- auto pb = variable_bytes(property_length_);
- for (auto e : pb) {
- property_length_buf_.push_back(e);
- }
- remaining_length_ +=
- property_length_buf_.size() +
- property_length_;
- auto rb = remaining_bytes(remaining_length_);
- for (auto e : rb) {
- remaining_length_buf_.push_back(e);
- }
- }
- /**
- * @brief Create const buffer sequence
- * it is for boost asio APIs
- * @return const buffer sequence
- */
- std::vector<as::const_buffer> const_buffer_sequence() const {
- std::vector<as::const_buffer> ret;
- ret.reserve(num_of_const_buffer_sequence());
- ret.emplace_back(as::buffer(&fixed_header_, 1));
- ret.emplace_back(as::buffer(remaining_length_buf_.data(), remaining_length_buf_.size()));
- ret.emplace_back(as::buffer(packet_id_.data(), packet_id_.size()));
- ret.emplace_back(as::buffer(property_length_buf_.data(), property_length_buf_.size()));
- for (auto const& p : props_) {
- v5::add_const_buffer_sequence(ret, p);
- }
- ret.emplace_back(as::buffer(reinterpret_cast<char const*>(reason_codes_.data()), reason_codes_.size()));
- return ret;
- }
- /**
- * @brief Get whole size of sequence
- * @return whole size
- */
- std::size_t size() const {
- return
- 1 + // fixed header
- remaining_length_buf_.size() +
- remaining_length_;
- }
- /**
- * @brief Get number of element of const_buffer_sequence
- * @return number of element of const_buffer_sequence
- */
- constexpr std::size_t num_of_const_buffer_sequence() const {
- return num_of_const_buffer_sequence_;
- }
- /**
- * @brief Create one continuours buffer.
- * All sequence of buffers are concatinated.
- * It is useful to store to file/database.
- * @return continuous buffer
- */
- std::string continuous_buffer() const {
- std::string ret;
- ret.reserve(size());
- ret.push_back(static_cast<char>(fixed_header_));
- ret.append(remaining_length_buf_.data(), remaining_length_buf_.size());
- ret.append(packet_id_.data(), packet_id_.size());
- auto it = ret.end();
- ret.resize(ret.size() + property_length_);
- auto end = ret.end();
- for (auto const& p : props_) {
- v5::fill(p, it, end);
- it += static_cast<std::string::difference_type>(v5::size(p));
- }
- ret.append(reinterpret_cast<char const*>(reason_codes_.data()), reason_codes_.size());
- return ret;
- }
- private:
- std::uint8_t fixed_header_;
- std::vector<v5::unsuback_reason_code> reason_codes_;
- boost::container::static_vector<char, PacketIdBytes> packet_id_;
- std::size_t remaining_length_;
- boost::container::static_vector<char, 4> remaining_length_buf_;
- std::size_t property_length_;
- boost::container::static_vector<char, 4> property_length_buf_;
- properties props_;
- std::size_t num_of_const_buffer_sequence_;
- };
- using unsuback_message = basic_unsuback_message<2>;
- struct pingreq_message : detail::header_only_message {
- pingreq_message()
- : detail::header_only_message(control_packet_type::pingreq, 0b0000)
- {}
- };
- struct pingresp_message : detail::header_only_message {
- pingresp_message()
- : detail::header_only_message(control_packet_type::pingresp, 0b0000)
- {}
- };
- struct disconnect_message {
- disconnect_message(
- v5::disconnect_reason_code reason_code,
- properties props
- )
- : fixed_header_(make_fixed_header(control_packet_type::disconnect, 0b0000)),
- remaining_length_(0),
- reason_code_(reason_code),
- property_length_(
- std::accumulate(
- props.begin(),
- props.end(),
- std::size_t(0U),
- [](std::size_t total, property_variant const& pv) {
- return total + v5::size(pv);
- }
- )
- ),
- props_(force_move(props)),
- num_of_const_buffer_sequence_(
- 1 + // fixed header
- 1 + // remaining length
- (
- // TODO: This is wrong. The reason code MUST be provided
- // if there are properties. Not the other way around.
- // https://docs.oasis-open.org/mqtt/mqtt/v5.0/os/mqtt-v5.0-os.html#_Toc3901144
- // 3.14.2.1 Disconnect Reason Code
- // The Reason Code and Property Length can be omitted if
- // the Reason Code is 0x00 (Normal disconnecton) and there are no
- // Properties. In this case the DISCONNECT has a Remaining Length of 0.
- reason_code_ != v5::disconnect_reason_code::normal_disconnection || MQTT_ALWAYS_SEND_REASON_CODE ? (
- 1 + // reason code
- 1 + // property length
- std::accumulate(
- props_.begin(),
- props_.end(),
- std::size_t(0U),
- [](std::size_t total, property_variant const& pv) {
- return total + v5::num_of_const_buffer_sequence(pv);
- }
- )
- )
- : 0
- )
- )
- {
- auto pb = variable_bytes(property_length_);
- for (auto e : pb) {
- property_length_buf_.push_back(e);
- }
- // TODO: This is wrong. The reason code MUST be provided
- // if there are properties. Not the other way around.
- // https://docs.oasis-open.org/mqtt/mqtt/v5.0/os/mqtt-v5.0-os.html#_Toc3901144
- // 3.14.2.1 Disconnect Reason Code
- // The Reason Code and Property Length can be omitted if
- // the Reason Code is 0x00 (Normal disconnecton) and there are no
- // Properties. In this case the DISCONNECT has a Remaining Length of 0.
- if (reason_code_ != v5::disconnect_reason_code::normal_disconnection || MQTT_ALWAYS_SEND_REASON_CODE) {
- remaining_length_ =
- 1 + // reason code
- property_length_buf_.size() +
- property_length_;
- }
- auto rb = remaining_bytes(remaining_length_);
- for (auto e : rb) {
- remaining_length_buf_.push_back(e);
- }
- }
- /**
- * @brief Create const buffer sequence
- * it is for boost asio APIs
- * @return const buffer sequence
- */
- std::vector<as::const_buffer> const_buffer_sequence() const {
- std::vector<as::const_buffer> ret;
- ret.reserve(num_of_const_buffer_sequence());
- ret.emplace_back(as::buffer(&fixed_header_, 1));
- ret.emplace_back(as::buffer(remaining_length_buf_.data(), remaining_length_buf_.size()));
- // TODO: This is wrong. The reason code MUST be provided
- // if there are properties. Not the other way around.
- // https://docs.oasis-open.org/mqtt/mqtt/v5.0/os/mqtt-v5.0-os.html#_Toc3901144
- // 3.14.2.1 Disconnect Reason Code
- // The Reason Code and Property Length can be omitted if
- // the Reason Code is 0x00 (Normal disconnecton) and there are no
- // Properties. In this case the DISCONNECT has a Remaining Length of 0.
- if (reason_code_ != v5::disconnect_reason_code::normal_disconnection || MQTT_ALWAYS_SEND_REASON_CODE) {
- ret.emplace_back(as::buffer(&reason_code_, 1));
- ret.emplace_back(as::buffer(property_length_buf_.data(), property_length_buf_.size()));
- for (auto const& p : props_) {
- v5::add_const_buffer_sequence(ret, p);
- }
- }
- return ret;
- }
- /**
- * @brief Get whole size of sequence
- * @return whole size
- */
- std::size_t size() const {
- return
- 1 + // fixed header
- remaining_length_buf_.size() +
- remaining_length_;
- }
- /**
- * @brief Get number of element of const_buffer_sequence
- * @return number of element of const_buffer_sequence
- */
- constexpr std::size_t num_of_const_buffer_sequence() const {
- return num_of_const_buffer_sequence_;
- }
- /**
- * @brief Create one continuours buffer.
- * All sequence of buffers are concatinated.
- * It is useful to store to file/database.
- * @return continuous buffer
- */
- std::string continuous_buffer() const {
- std::string ret;
- ret.reserve(size());
- ret.push_back(static_cast<char>(fixed_header_));
- ret.append(remaining_length_buf_.data(), remaining_length_buf_.size());
- // TODO: This is wrong. The reason code MUST be provided
- // if there are properties. Not the other way around.
- // https://docs.oasis-open.org/mqtt/mqtt/v5.0/os/mqtt-v5.0-os.html#_Toc3901144
- // 3.14.2.1 Disconnect Reason Code
- // The Reason Code and Property Length can be omitted if
- // the Reason Code is 0x00 (Normal disconnecton) and there are no
- // Properties. In this case the DISCONNECT has a Remaining Length of 0.
- if (reason_code_ != v5::disconnect_reason_code::normal_disconnection || MQTT_ALWAYS_SEND_REASON_CODE) {
- ret.push_back(static_cast<char>(reason_code_));
- auto it = ret.end();
- ret.resize(ret.size() + property_length_);
- auto end = ret.end();
- for (auto const& p : props_) {
- v5::fill(p, it, end);
- it += static_cast<std::string::difference_type>(v5::size(p));
- }
- }
- return ret;
- }
- private:
- std::uint8_t fixed_header_;
- std::size_t remaining_length_;
- boost::container::static_vector<char, 4> remaining_length_buf_;
- v5::disconnect_reason_code reason_code_;
- std::size_t property_length_;
- boost::container::static_vector<char, 4> property_length_buf_;
- properties props_;
- std::size_t num_of_const_buffer_sequence_;
- };
- struct auth_message {
- auth_message(
- v5::auth_reason_code reason_code,
- properties props
- )
- : fixed_header_(make_fixed_header(control_packet_type::auth, 0b0000)),
- remaining_length_(0),
- reason_code_(reason_code),
- property_length_(
- std::accumulate(
- props.begin(),
- props.end(),
- std::size_t(0U),
- [](std::size_t total, property_variant const& pv) {
- return total + v5::size(pv);
- }
- )
- ),
- props_(force_move(props)),
- num_of_const_buffer_sequence_(
- 1 + // fixed header
- 1 + // remaining length
- (
- // TODO: This is wrong. The reason code MUST be provided
- // if there are properties. Not the other way around.
- // https://docs.oasis-open.org/mqtt/mqtt/v5.0/os/mqtt-v5.0-os.html#_Toc3901220
- // 3.15.2.1 Authenticate Reason Code
- // The Reason Code and Property Length can be omitted if
- // the Reason Code is 0x00 (Success) and there are no
- // Properties. In this case the AUTH has a Remaining Length of 0.
- reason_code_ != v5::auth_reason_code::success || MQTT_ALWAYS_SEND_REASON_CODE ?
- (
- 1 + // reason code
- 1 + // property length
- std::accumulate(
- props_.begin(),
- props_.end(),
- std::size_t(0U),
- [](std::size_t total, property_variant const& pv) {
- return total + v5::num_of_const_buffer_sequence(pv);
- }
- )
- )
- : 0
- )
- )
- {
- auto pb = variable_bytes(property_length_);
- for (auto e : pb) {
- property_length_buf_.push_back(e);
- }
- // TODO: This is wrong. The reason code MUST be provided
- // if there are properties. Not the other way around.
- // https://docs.oasis-open.org/mqtt/mqtt/v5.0/os/mqtt-v5.0-os.html#_Toc3901220
- // 3.15.2.1 Authenticate Reason Code
- // The Reason Code and Property Length can be omitted if
- // the Reason Code is 0x00 (Success) and there are no
- // Properties. In this case the AUTH has a Remaining Length of 0.
- if (reason_code_ != v5::auth_reason_code::success || MQTT_ALWAYS_SEND_REASON_CODE) {
- remaining_length_ =
- 1 + // reason code
- property_length_buf_.size() +
- property_length_;
- }
- auto rb = remaining_bytes(remaining_length_);
- for (auto e : rb) {
- remaining_length_buf_.push_back(e);
- }
- }
- /**
- * @brief Create const buffer sequence
- * it is for boost asio APIs
- * @return const buffer sequence
- */
- std::vector<as::const_buffer> const_buffer_sequence() const {
- std::vector<as::const_buffer> ret;
- ret.reserve(num_of_const_buffer_sequence());
- ret.emplace_back(as::buffer(&fixed_header_, 1));
- ret.emplace_back(as::buffer(remaining_length_buf_.data(), remaining_length_buf_.size()));
- // TODO: This is wrong. The reason code MUST be provided
- // if there are properties. Not the other way around.
- // https://docs.oasis-open.org/mqtt/mqtt/v5.0/os/mqtt-v5.0-os.html#_Toc3901220
- // 3.15.2.1 Authenticate Reason Code
- // The Reason Code and Property Length can be omitted if
- // the Reason Code is 0x00 (Success) and there are no
- // Properties. In this case the AUTH has a Remaining Length of 0.
- if (reason_code_ != v5::auth_reason_code::success || MQTT_ALWAYS_SEND_REASON_CODE) {
- ret.emplace_back(as::buffer(&reason_code_, 1));
- ret.emplace_back(as::buffer(property_length_buf_.data(), property_length_buf_.size()));
- for (auto const& p : props_) {
- v5::add_const_buffer_sequence(ret, p);
- }
- }
- return ret;
- }
- /**
- * @brief Get whole size of sequence
- * @return whole size
- */
- std::size_t size() const {
- return
- 1 + // fixed header
- remaining_length_buf_.size() +
- remaining_length_;
- }
- /**
- * @brief Get number of element of const_buffer_sequence
- * @return number of element of const_buffer_sequence
- */
- constexpr std::size_t num_of_const_buffer_sequence() const {
- return num_of_const_buffer_sequence_;
- }
- /**
- * @brief Create one continuours buffer.
- * All sequence of buffers are concatinated.
- * It is useful to store to file/database.
- * @return continuous buffer
- */
- std::string continuous_buffer() const {
- std::string ret;
- ret.reserve(size());
- ret.push_back(static_cast<char>(fixed_header_));
- ret.append(remaining_length_buf_.data(), remaining_length_buf_.size());
- // TODO: This is wrong. The reason code MUST be provided
- // if there are properties. Not the other way around.
- // https://docs.oasis-open.org/mqtt/mqtt/v5.0/os/mqtt-v5.0-os.html#_Toc3901220
- // 3.15.2.1 Authenticate Reason Code
- // The Reason Code and Property Length can be omitted if
- // the Reason Code is 0x00 (Success) and there are no
- // Properties. In this case the AUTH has a Remaining Length of 0.
- if (reason_code_ != v5::auth_reason_code::success || MQTT_ALWAYS_SEND_REASON_CODE) {
- ret.push_back(static_cast<char>(reason_code_));
- auto it = ret.end();
- ret.resize(ret.size() + property_length_);
- auto end = ret.end();
- for (auto const& p : props_) {
- v5::fill(p, it, end);
- it += static_cast<std::string::difference_type>(v5::size(p));
- }
- }
- return ret;
- }
- private:
- std::uint8_t fixed_header_;
- std::size_t remaining_length_;
- boost::container::static_vector<char, 4> remaining_length_buf_;
- v5::auth_reason_code reason_code_;
- std::size_t property_length_;
- boost::container::static_vector<char, 4> property_length_buf_;
- properties props_;
- std::size_t num_of_const_buffer_sequence_;
- };
- } // namespace v5
- } // namespace MQTT_NS
- #endif // MQTT_V5_MESSAGE_HPP
|