Commit 03fd22bd47dd54ffb2e3b2127c585f20c7099f4b
1 parent
34929680
Check for publish message expiration
Showing
6 changed files
with
54 additions
and
7 deletions
qospacketqueue.cpp
| ... | ... | @@ -51,6 +51,11 @@ void QoSPublishQueue::erase(const uint16_t packet_id) |
| 51 | 51 | } |
| 52 | 52 | } |
| 53 | 53 | |
| 54 | +std::list<QueuedPublish>::const_iterator QoSPublishQueue::erase(std::list<QueuedPublish>::const_iterator pos) | |
| 55 | +{ | |
| 56 | + return this->queue.erase(pos); | |
| 57 | +} | |
| 58 | + | |
| 54 | 59 | size_t QoSPublishQueue::size() const |
| 55 | 60 | { |
| 56 | 61 | return queue.size(); | ... | ... |
qospacketqueue.h
| ... | ... | @@ -31,6 +31,7 @@ class QoSPublishQueue |
| 31 | 31 | |
| 32 | 32 | public: |
| 33 | 33 | void erase(const uint16_t packet_id); |
| 34 | + std::list<QueuedPublish>::const_iterator erase(std::list<QueuedPublish>::const_iterator pos); | |
| 34 | 35 | size_t size() const; |
| 35 | 36 | size_t getByteSize() const; |
| 36 | 37 | void queuePublish(PublishCopyFactory ©Factory, uint16_t id, char new_max_qos); | ... | ... |
session.cpp
| ... | ... | @@ -210,12 +210,22 @@ void Session::clearQosMessage(uint16_t packet_id) |
| 210 | 210 | } |
| 211 | 211 | } |
| 212 | 212 | |
| 213 | -// [MQTT-4.4.0-1]: "When a Client reconnects with CleanSession set to 0, both the Client and Server MUST re-send any | |
| 214 | -// unacknowledged PUBLISH Packets (where QoS > 0) and PUBREL Packets using their original Packet Identifiers. This | |
| 215 | -// is the only circumstance where a Client or Server is REQUIRED to redeliver messages." | |
| 216 | -// | |
| 217 | -// There is a bit of a hole there, I think. When we write out a packet to a receiver, it may decide to drop it, if its buffers | |
| 218 | -// are full, for instance. We are not required to (periodically) retry. TODO Perhaps I will implement that retry anyway. | |
| 213 | + | |
| 214 | +/** | |
| 215 | + * @brief Session::sendPendingQosMessages sends pending publishes and QoS2 control packets. | |
| 216 | + * @return the amount of messages/packets published. | |
| 217 | + * | |
| 218 | + * [MQTT-4.4.0-1] (about MQTT 3.1.1): "When a Client reconnects with CleanSession set to 0, both the Client and Server MUST | |
| 219 | + * re-send any unacknowledged PUBLISH Packets (where QoS > 0) and PUBREL Packets using their original Packet Identifiers. This | |
| 220 | + * is the only circumstance where a Client or Server is REQUIRED to redeliver messages." | |
| 221 | + * | |
| 222 | + * Only MQTT 3.1 requires retransmission. MQTT 3.1.1 and MQTT 5 only send on reconnect. At time of writing this comment, | |
| 223 | + * FlashMQ doesn't have a retransmission system. I don't think I want to implement one for the sake of 3.1 compliance, | |
| 224 | + * because it's just not that great an idea in terms of server load and quality of modern TCP. However, receiving clients | |
| 225 | + * can still decide to drop packets, like when their buffers are full. The clients from where the packet originates will | |
| 226 | + * never know that, because IT will have received the PUBACK from FlashMQ. The QoS system is not between publisher | |
| 227 | + * and subscriber. Users are required to implement something themselves. | |
| 228 | + */ | |
| 219 | 229 | uint64_t Session::sendPendingQosMessages() |
| 220 | 230 | { |
| 221 | 231 | uint64_t count = 0; |
| ... | ... | @@ -224,8 +234,18 @@ uint64_t Session::sendPendingQosMessages() |
| 224 | 234 | if (c) |
| 225 | 235 | { |
| 226 | 236 | std::lock_guard<std::mutex> locker(qosQueueMutex); |
| 227 | - for (const QueuedPublish &queuedPublish : qosPacketQueue) | |
| 237 | + | |
| 238 | + auto pos = qosPacketQueue.begin(); | |
| 239 | + while (pos != qosPacketQueue.end()) | |
| 228 | 240 | { |
| 241 | + const QueuedPublish &queuedPublish = *pos; | |
| 242 | + | |
| 243 | + if (queuedPublish.getPublish().hasExpired()) | |
| 244 | + { | |
| 245 | + pos = qosPacketQueue.erase(pos); | |
| 246 | + continue; | |
| 247 | + } | |
| 248 | + | |
| 229 | 249 | MqttPacket p(c->getProtocolVersion(), queuedPublish.getPublish()); |
| 230 | 250 | p.setDuplicate(); |
| 231 | 251 | ... | ... |
subscriptionstore.cpp
| ... | ... | @@ -272,6 +272,15 @@ std::shared_ptr<Session> SubscriptionStore::lockSession(const std::string &clien |
| 272 | 272 | return std::shared_ptr<Session>(); |
| 273 | 273 | } |
| 274 | 274 | |
| 275 | +/** | |
| 276 | + * @brief SubscriptionStore::sendQueuedWillMessages sends queued will messages. | |
| 277 | + * | |
| 278 | + * The list of pendingWillMessages is sorted. This allows for fast insertion and dequeueing of wills that have expired. | |
| 279 | + * | |
| 280 | + * The expiry interval as set in the properties of the will message is not used to check for expiration here. To | |
| 281 | + * quote the specs: "If present, the Four Byte value is the lifetime of the Will Message in seconds and is sent as | |
| 282 | + * the Publication Expiry Interval when the Server publishes the Will Message." | |
| 283 | + */ | |
| 275 | 284 | void SubscriptionStore::sendQueuedWillMessages() |
| 276 | 285 | { |
| 277 | 286 | const auto now = std::chrono::steady_clock::now(); |
| ... | ... | @@ -294,6 +303,11 @@ void SubscriptionStore::sendQueuedWillMessages() |
| 294 | 303 | } |
| 295 | 304 | } |
| 296 | 305 | |
| 306 | +/** | |
| 307 | + * @brief SubscriptionStore::queueWillMessage queues the will message by bin-searching its place in the sorted list. | |
| 308 | + * @param willMessage | |
| 309 | + * @param forceNow | |
| 310 | + */ | |
| 297 | 311 | void SubscriptionStore::queueWillMessage(std::shared_ptr<Publish> &willMessage, bool forceNow) |
| 298 | 312 | { |
| 299 | 313 | if (!willMessage) | ... | ... |
types.cpp
| ... | ... | @@ -170,6 +170,12 @@ bool PublishBase::hasUserProperties() const |
| 170 | 170 | return this->propertyBuilder.operator bool() && this->propertyBuilder->getUserProperties().operator bool(); |
| 171 | 171 | } |
| 172 | 172 | |
| 173 | +bool PublishBase::hasExpired() const | |
| 174 | +{ | |
| 175 | + const std::chrono::seconds age = std::chrono::duration_cast<std::chrono::seconds>(std::chrono::steady_clock::now() - this->createdAt); | |
| 176 | + return (expiresAfter > age); | |
| 177 | +} | |
| 178 | + | |
| 173 | 179 | Publish::Publish(const Publish &other) : |
| 174 | 180 | PublishBase(other) |
| 175 | 181 | { | ... | ... |