async_mqtt 5.0.0
Loading...
Searching...
No Matches
v5_disconnect.hpp
1// Copyright Takatoshi Kondo 2022
2//
3// Distributed under the Boost Software License, Version 1.0.
4// (See accompanying file LICENSE_1_0.txt or copy at
5// http://www.boost.org/LICENSE_1_0.txt)
6
7#if !defined(ASYNC_MQTT_PACKET_V5_DISCONNECT_HPP)
8#define ASYNC_MQTT_PACKET_V5_DISCONNECT_HPP
9
10#include <utility>
11#include <numeric>
12
13#include <boost/numeric/conversion/cast.hpp>
14
15#include <async_mqtt/exception.hpp>
16#include <async_mqtt/buffer.hpp>
17
18#include <async_mqtt/util/move.hpp>
19#include <async_mqtt/util/static_vector.hpp>
20#include <async_mqtt/util/endian_convert.hpp>
21#include <async_mqtt/util/scope_guard.hpp>
22
23#include <async_mqtt/packet/fixed_header.hpp>
24#include <async_mqtt/packet/packet_id_type.hpp>
25#include <async_mqtt/packet/reason_code.hpp>
26#include <async_mqtt/packet/property_variant.hpp>
27#include <async_mqtt/packet/copy_to_static_vector.hpp>
28
29namespace async_mqtt::v5 {
30
31namespace as = boost::asio;
32
46public:
56 disconnect_reason_code reason_code,
57 properties props
59 optional<disconnect_reason_code>(reason_code),
60 force_move(props)
61 }
62 {}
63
69 nullopt,
70 properties{}
71 }
72 {}
73
81 disconnect_reason_code reason_code
83 optional<disconnect_reason_code>(reason_code),
84 properties{}
85 }
86 {}
87
89 // fixed_header
90 if (buf.empty()) {
91 throw make_error(
92 errc::bad_message,
93 "v5::disconnect_packet fixed_header doesn't exist"
94 );
95 }
96 fixed_header_ = static_cast<std::uint8_t>(buf.front());
97 buf.remove_prefix(1);
98
99 // remaining_length
100 if (auto vl_opt = insert_advance_variable_length(buf, remaining_length_buf_)) {
101 remaining_length_ = *vl_opt;
102 }
103 else {
104 throw make_error(errc::bad_message, "v5::disconnect_packet remaining length is invalid");
105 }
106
107 if (remaining_length_ == 0) {
108 if (!buf.empty()) {
109 throw make_error(errc::bad_message, "v5::disconnect_packet remaining length is invalid");
110 }
111 return;
112 }
113
114 // reason_code
115 reason_code_.emplace(static_cast<disconnect_reason_code>(buf.front()));
116 buf.remove_prefix(1);
117 switch (*reason_code_) {
118 case disconnect_reason_code::normal_disconnection:
119 case disconnect_reason_code::disconnect_with_will_message:
120 case disconnect_reason_code::unspecified_error:
121 case disconnect_reason_code::malformed_packet:
122 case disconnect_reason_code::protocol_error:
123 case disconnect_reason_code::implementation_specific_error:
124 case disconnect_reason_code::not_authorized:
125 case disconnect_reason_code::server_busy:
126 case disconnect_reason_code::server_shutting_down:
127 case disconnect_reason_code::keep_alive_timeout:
128 case disconnect_reason_code::session_taken_over:
129 case disconnect_reason_code::topic_filter_invalid:
130 case disconnect_reason_code::topic_name_invalid:
131 case disconnect_reason_code::receive_maximum_exceeded:
132 case disconnect_reason_code::topic_alias_invalid:
133 case disconnect_reason_code::packet_too_large:
134 case disconnect_reason_code::message_rate_too_high:
135 case disconnect_reason_code::quota_exceeded:
136 case disconnect_reason_code::administrative_action:
137 case disconnect_reason_code::payload_format_invalid:
138 case disconnect_reason_code::retain_not_supported:
139 case disconnect_reason_code::qos_not_supported:
140 case disconnect_reason_code::use_another_server:
141 case disconnect_reason_code::server_moved:
142 case disconnect_reason_code::shared_subscriptions_not_supported:
143 case disconnect_reason_code::connection_rate_exceeded:
144 case disconnect_reason_code::maximum_connect_time:
145 case disconnect_reason_code::subscription_identifiers_not_supported:
146 case disconnect_reason_code::wildcard_subscriptions_not_supported:
147 break;
148 default:
149 throw make_error(
150 errc::bad_message,
151 "v5::disconnect_packet connect reason_code is invalid"
152 );
153 break;
154 }
155
156 if (remaining_length_ == 1) {
157 if (!buf.empty()) {
158 throw make_error(errc::bad_message, "v5::disconnect_packet remaining length is invalid");
159 }
160 return;
161 }
162
163 // property
164 auto it = buf.begin();
165 if (auto pl_opt = variable_bytes_to_val(it, buf.end())) {
166 property_length_ = *pl_opt;
167 std::copy(buf.begin(), it, std::back_inserter(property_length_buf_));
168 buf.remove_prefix(std::size_t(std::distance(buf.begin(), it)));
169 if (buf.size() < property_length_) {
170 throw make_error(
171 errc::bad_message,
172 "v5::disconnect_packet properties_don't match its length"
173 );
174 }
175 auto prop_buf = buf.substr(0, property_length_);
176 props_ = make_properties(prop_buf, property_location::disconnect);
177 buf.remove_prefix(property_length_);
178 }
179 else {
180 throw make_error(
181 errc::bad_message,
182 "v5::disconnect_packet property_length is invalid"
183 );
184 }
185
186 if (!buf.empty()) {
187 throw make_error(
188 errc::bad_message,
189 "v5::disconnect_packet properties don't match its length"
190 );
191 }
192 }
193
194 constexpr control_packet_type type() const {
195 return control_packet_type::disconnect;
196 }
197
203 std::vector<as::const_buffer> const_buffer_sequence() const {
204 std::vector<as::const_buffer> ret;
206 ret.emplace_back(as::buffer(&fixed_header_, 1));
207 ret.emplace_back(as::buffer(remaining_length_buf_.data(), remaining_length_buf_.size()));
208
209 if (reason_code_) {
210 ret.emplace_back(as::buffer(&*reason_code_, 1));
211
212 if (property_length_buf_.size() != 0) {
213 ret.emplace_back(as::buffer(property_length_buf_.data(), property_length_buf_.size()));
214 auto props_cbs = async_mqtt::const_buffer_sequence(props_);
215 std::move(props_cbs.begin(), props_cbs.end(), std::back_inserter(ret));
216 }
217 }
218
219 return ret;
220 }
221
226 std::size_t size() const {
227 return
228 1 + // fixed header
229 remaining_length_buf_.size() +
230 remaining_length_;
231 }
232
237 std::size_t num_of_const_buffer_sequence() const {
238 return
239 1 + // fixed header
240 1 + // remaining length
241 [&] () -> std::size_t {
242 if (reason_code_) {
243 return
244 1 + // reason_code
245 [&] () -> std::size_t {
246 if (property_length_buf_.size() == 0) return 0;
247 return
248 1 + // property length
249 async_mqtt::num_of_const_buffer_sequence(props_);
250 }();
251 }
252 return 0;
253 }();
254 }
255
260 disconnect_reason_code code() const {
261 if (reason_code_) return *reason_code_;
262 return disconnect_reason_code::normal_disconnection;
263 }
264
269 properties const& props() const {
270 return props_;
271 }
272
273 friend
274 inline std::ostream& operator<<(std::ostream& o, disconnect_packet const& v) {
275 o <<
276 "v5::disconnect{";
277 if (v.reason_code_) {
278 o << "rc:" << *v.reason_code_;
279 }
280 if (!v.props().empty()) {
281 o << ",ps:" << v.props();
282 };
283 o << "}";
284 return o;
285 }
286
287private:
289 optional<disconnect_reason_code> reason_code,
290 properties props
291 )
292 : fixed_header_{
293 make_fixed_header(control_packet_type::disconnect, 0b0000)
294 },
295 remaining_length_{
296 0
297 },
298 reason_code_{reason_code},
299 property_length_(async_mqtt::size(props)),
300 props_(force_move(props))
301 {
302 using namespace std::literals;
303
304 auto guard = unique_scope_guard(
305 [&] {
306 auto rb = val_to_variable_bytes(boost::numeric_cast<std::uint32_t>(remaining_length_));
307 for (auto e : rb) {
308 remaining_length_buf_.push_back(e);
309 }
310 }
311 );
312
313 if (!reason_code_) return;
314 remaining_length_ += 1;
315
316 if (property_length_ == 0) return;
317
318 auto pb = val_to_variable_bytes(boost::numeric_cast<std::uint32_t>(property_length_));
319 for (auto e : pb) {
320 property_length_buf_.push_back(e);
321 }
322
323 for (auto const& prop : props_) {
324 auto id = prop.id();
325 if (!validate_property(property_location::disconnect, id)) {
326 throw make_error(
327 errc::bad_message,
328 "v5::disconnect_packet property "s + id_to_str(id) + " is not allowed"
329 );
330 }
331 }
332
333 remaining_length_ += property_length_buf_.size() + property_length_;
334 }
335
336private:
337 std::uint8_t fixed_header_;
338 std::size_t remaining_length_;
339 static_vector<char, 4> remaining_length_buf_;
340
341 optional<disconnect_reason_code> reason_code_;
342
343 std::size_t property_length_ = 0;
344 static_vector<char, 4> property_length_buf_;
345 properties props_;
346};
347
348} // namespace async_mqtt::v5
349
350#endif // ASYNC_MQTT_PACKET_V5_DISCONNECT_HPP
Definition packet_variant.hpp:49
buffer that has string_view interface This class provides string_view interface. This class hold stri...
Definition buffer.hpp:30
buffer substr(size_type pos=0, size_type count=npos) const &
get substring The returned buffer ragnge is the same as string_view::substr(). In addition the lifeti...
Definition buffer.hpp:201
MQTT DISCONNECT packet (v5)
Definition v5_disconnect.hpp:45
properties const & props() const
Definition v5_disconnect.hpp:269
disconnect_reason_code code() const
Definition v5_disconnect.hpp:260
std::size_t size() const
Get packet size.
Definition v5_disconnect.hpp:226
disconnect_packet(disconnect_reason_code reason_code, properties props)
constructor
Definition v5_disconnect.hpp:55
disconnect_packet()
constructor
Definition v5_disconnect.hpp:67
std::size_t num_of_const_buffer_sequence() const
Get number of element of const_buffer_sequence.
Definition v5_disconnect.hpp:237
std::vector< as::const_buffer > const_buffer_sequence() const
Create const buffer sequence it is for boost asio APIs.
Definition v5_disconnect.hpp:203
disconnect_packet(disconnect_reason_code reason_code)
constructor
Definition v5_disconnect.hpp:80