Commit d0333fb6092a291fa3dd114fa6a1dcf6f47c251b
1 parent
436a5ffa
Add configurable session expiration
Showing
8 changed files
with
39 additions
and
20 deletions
configfileparser.cpp
| @@ -105,6 +105,7 @@ ConfigFileParser::ConfigFileParser(const std::string &path) : | @@ -105,6 +105,7 @@ ConfigFileParser::ConfigFileParser(const std::string &path) : | ||
| 105 | validKeys.insert("mosquitto_acl_file"); | 105 | validKeys.insert("mosquitto_acl_file"); |
| 106 | validKeys.insert("allow_anonymous"); | 106 | validKeys.insert("allow_anonymous"); |
| 107 | validKeys.insert("rlimit_nofile"); | 107 | validKeys.insert("rlimit_nofile"); |
| 108 | + validKeys.insert("expire_sessions_after_seconds"); | ||
| 108 | 109 | ||
| 109 | validListenKeys.insert("port"); | 110 | validListenKeys.insert("port"); |
| 110 | validListenKeys.insert("protocol"); | 111 | validListenKeys.insert("protocol"); |
| @@ -390,6 +391,16 @@ void ConfigFileParser::loadFile(bool test) | @@ -390,6 +391,16 @@ void ConfigFileParser::loadFile(bool test) | ||
| 390 | } | 391 | } |
| 391 | tmpSettings->rlimitNoFile = newVal; | 392 | tmpSettings->rlimitNoFile = newVal; |
| 392 | } | 393 | } |
| 394 | + | ||
| 395 | + if (key == "expire_sessions_after_seconds") | ||
| 396 | + { | ||
| 397 | + int64_t newVal = std::stoi(value); | ||
| 398 | + if (newVal < 0 || (newVal > 0 && newVal <= 300)) // 0 means disable | ||
| 399 | + { | ||
| 400 | + throw ConfigFileException(formatString("expire_sessions_after_seconds value '%d' is invalid. Valid values are 0, or 300 or higher.", newVal)); | ||
| 401 | + } | ||
| 402 | + tmpSettings->expireSessionsAfterSeconds = newVal; | ||
| 403 | + } | ||
| 393 | } | 404 | } |
| 394 | } | 405 | } |
| 395 | catch (std::invalid_argument &ex) // catch for the stoi() | 406 | catch (std::invalid_argument &ex) // catch for the stoi() |
mainapp.cpp
| @@ -179,8 +179,13 @@ MainApp::MainApp(const std::string &configFilePath) : | @@ -179,8 +179,13 @@ MainApp::MainApp(const std::string &configFilePath) : | ||
| 179 | // TODO: override in conf possibility. | 179 | // TODO: override in conf possibility. |
| 180 | logger->logf(LOG_NOTICE, "%d CPUs are detected, making as many threads.", num_threads); | 180 | logger->logf(LOG_NOTICE, "%d CPUs are detected, making as many threads.", num_threads); |
| 181 | 181 | ||
| 182 | - auto f = std::bind(&MainApp::queueCleanup, this); | ||
| 183 | - timer.addCallback(f, 86400000, "session expiration"); | 182 | + if (settings->expireSessionsAfterSeconds > 0) |
| 183 | + { | ||
| 184 | + auto f = std::bind(&MainApp::queueCleanup, this); | ||
| 185 | + const uint64_t derrivedSessionCheckInterval = std::max<uint64_t>((settings->expireSessionsAfterSeconds)*1000*2, 600000); | ||
| 186 | + const uint64_t sessionCheckInterval = std::min<uint64_t>(derrivedSessionCheckInterval, 86400000); | ||
| 187 | + timer.addCallback(f, sessionCheckInterval, "session expiration"); | ||
| 188 | + } | ||
| 184 | 189 | ||
| 185 | auto fKeepAlive = std::bind(&MainApp::queueKeepAliveCheckAtAllThreads, this); | 190 | auto fKeepAlive = std::bind(&MainApp::queueKeepAliveCheckAtAllThreads, this); |
| 186 | timer.addCallback(fKeepAlive, 30000, "keep-alive check"); | 191 | timer.addCallback(fKeepAlive, 30000, "keep-alive check"); |
| @@ -726,7 +731,7 @@ void MainApp::queueCleanup() | @@ -726,7 +731,7 @@ void MainApp::queueCleanup() | ||
| 726 | { | 731 | { |
| 727 | std::lock_guard<std::mutex> locker(eventMutex); | 732 | std::lock_guard<std::mutex> locker(eventMutex); |
| 728 | 733 | ||
| 729 | - auto f = std::bind(&SubscriptionStore::removeExpiredSessionsClients, subscriptionStore.get()); | 734 | + auto f = std::bind(&SubscriptionStore::removeExpiredSessionsClients, subscriptionStore.get(), settings->expireSessionsAfterSeconds); |
| 730 | taskQueue.push_front(f); | 735 | taskQueue.push_front(f); |
| 731 | 736 | ||
| 732 | wakeUpThread(); | 737 | wakeUpThread(); |
session.cpp
| @@ -164,17 +164,23 @@ uint64_t Session::sendPendingQosMessages() | @@ -164,17 +164,23 @@ uint64_t Session::sendPendingQosMessages() | ||
| 164 | return count; | 164 | return count; |
| 165 | } | 165 | } |
| 166 | 166 | ||
| 167 | -std::chrono::time_point<std::chrono::steady_clock> zeroTime; | ||
| 168 | -std::chrono::seconds expireAfter(EXPIRE_SESSION_AFTER); | ||
| 169 | - | ||
| 170 | -void Session::touch(std::chrono::time_point<std::chrono::steady_clock> val) | 167 | +/** |
| 168 | + * @brief Session::touch with a time value allowed touching without causing another sys/lib call to get the time. | ||
| 169 | + * @param newval | ||
| 170 | + */ | ||
| 171 | +void Session::touch(std::chrono::time_point<std::chrono::steady_clock> newval) | ||
| 171 | { | 172 | { |
| 172 | - std::chrono::time_point<std::chrono::steady_clock> newval = val > zeroTime ? val : std::chrono::steady_clock::now(); | ||
| 173 | lastTouched = newval; | 173 | lastTouched = newval; |
| 174 | } | 174 | } |
| 175 | 175 | ||
| 176 | -bool Session::hasExpired() | 176 | +void Session::touch() |
| 177 | +{ | ||
| 178 | + lastTouched = std::chrono::steady_clock::now(); | ||
| 179 | +} | ||
| 180 | + | ||
| 181 | +bool Session::hasExpired(int expireAfterSeconds) | ||
| 177 | { | 182 | { |
| 183 | + std::chrono::seconds expireAfter(expireAfterSeconds); | ||
| 178 | std::chrono::time_point<std::chrono::steady_clock> now = std::chrono::steady_clock::now(); | 184 | std::chrono::time_point<std::chrono::steady_clock> now = std::chrono::steady_clock::now(); |
| 179 | return clientDisconnected() && (lastTouched + expireAfter) < now; | 185 | return clientDisconnected() && (lastTouched + expireAfter) < now; |
| 180 | } | 186 | } |
session.h
| @@ -30,9 +30,6 @@ License along with FlashMQ. If not, see <https://www.gnu.org/licenses/>. | @@ -30,9 +30,6 @@ License along with FlashMQ. If not, see <https://www.gnu.org/licenses/>. | ||
| 30 | #define MAX_QOS_MSG_PENDING_PER_CLIENT 32 | 30 | #define MAX_QOS_MSG_PENDING_PER_CLIENT 32 |
| 31 | #define MAX_QOS_BYTES_PENDING_PER_CLIENT 4096 | 31 | #define MAX_QOS_BYTES_PENDING_PER_CLIENT 4096 |
| 32 | 32 | ||
| 33 | -// TODO make setting | ||
| 34 | -#define EXPIRE_SESSION_AFTER 1209600 | ||
| 35 | - | ||
| 36 | struct QueuedQosPacket | 33 | struct QueuedQosPacket |
| 37 | { | 34 | { |
| 38 | uint16_t id; | 35 | uint16_t id; |
| @@ -67,8 +64,9 @@ public: | @@ -67,8 +64,9 @@ public: | ||
| 67 | void writePacket(const MqttPacket &packet, char max_qos, uint64_t &count); | 64 | void writePacket(const MqttPacket &packet, char max_qos, uint64_t &count); |
| 68 | void clearQosMessage(uint16_t packet_id); | 65 | void clearQosMessage(uint16_t packet_id); |
| 69 | uint64_t sendPendingQosMessages(); | 66 | uint64_t sendPendingQosMessages(); |
| 70 | - void touch(std::chrono::time_point<std::chrono::steady_clock> val = std::chrono::time_point<std::chrono::steady_clock>()); | ||
| 71 | - bool hasExpired(); | 67 | + void touch(std::chrono::time_point<std::chrono::steady_clock> val); |
| 68 | + void touch(); | ||
| 69 | + bool hasExpired(int expireAfterSeconds); | ||
| 72 | 70 | ||
| 73 | void addIncomingQoS2MessageId(uint16_t packet_id); | 71 | void addIncomingQoS2MessageId(uint16_t packet_id); |
| 74 | bool incomingQoS2MessageIdInTransit(uint16_t packet_id) const; | 72 | bool incomingQoS2MessageIdInTransit(uint16_t packet_id) const; |
settings.h
| @@ -46,6 +46,7 @@ public: | @@ -46,6 +46,7 @@ public: | ||
| 46 | std::string mosquittoAclFile; | 46 | std::string mosquittoAclFile; |
| 47 | bool allowAnonymous = false; | 47 | bool allowAnonymous = false; |
| 48 | int rlimitNoFile = 1000000; | 48 | int rlimitNoFile = 1000000; |
| 49 | + uint64_t expireSessionsAfterSeconds = 1209600; | ||
| 49 | std::list<std::shared_ptr<Listener>> listeners; // Default one is created later, when none are defined. | 50 | std::list<std::shared_ptr<Listener>> listeners; // Default one is created later, when none are defined. |
| 50 | 51 | ||
| 51 | AuthOptCompatWrap &getAuthOptsCompat(); | 52 | AuthOptCompatWrap &getAuthOptsCompat(); |
subscriptionstore.cpp
| @@ -417,7 +417,7 @@ int SubscriptionNode::cleanSubscriptions() | @@ -417,7 +417,7 @@ int SubscriptionNode::cleanSubscriptions() | ||
| 417 | } | 417 | } |
| 418 | 418 | ||
| 419 | // This is not MQTT compliant, but the standard doesn't keep real world constraints into account. | 419 | // This is not MQTT compliant, but the standard doesn't keep real world constraints into account. |
| 420 | -void SubscriptionStore::removeExpiredSessionsClients() | 420 | +void SubscriptionStore::removeExpiredSessionsClients(int expireSessionsAfterSeconds) |
| 421 | { | 421 | { |
| 422 | RWLockGuard lock_guard(&subscriptionsRwlock); | 422 | RWLockGuard lock_guard(&subscriptionsRwlock); |
| 423 | lock_guard.wrlock(); | 423 | lock_guard.wrlock(); |
| @@ -429,11 +429,9 @@ void SubscriptionStore::removeExpiredSessionsClients() | @@ -429,11 +429,9 @@ void SubscriptionStore::removeExpiredSessionsClients() | ||
| 429 | { | 429 | { |
| 430 | std::shared_ptr<Session> &session = session_it->second; | 430 | std::shared_ptr<Session> &session = session_it->second; |
| 431 | 431 | ||
| 432 | - if (session->hasExpired()) | 432 | + if (session->hasExpired(expireSessionsAfterSeconds)) |
| 433 | { | 433 | { |
| 434 | -#ifndef NDEBUG | ||
| 435 | logger->logf(LOG_DEBUG, "Removing expired session from store %s", session->getClientId().c_str()); | 434 | logger->logf(LOG_DEBUG, "Removing expired session from store %s", session->getClientId().c_str()); |
| 436 | -#endif | ||
| 437 | session_it = sessionsById.erase(session_it); | 435 | session_it = sessionsById.erase(session_it); |
| 438 | } | 436 | } |
| 439 | else | 437 | else |
subscriptionstore.h
| @@ -99,7 +99,7 @@ public: | @@ -99,7 +99,7 @@ public: | ||
| 99 | 99 | ||
| 100 | void setRetainedMessage(const std::string &topic, const std::string &payload, char qos); | 100 | void setRetainedMessage(const std::string &topic, const std::string &payload, char qos); |
| 101 | 101 | ||
| 102 | - void removeExpiredSessionsClients(); | 102 | + void removeExpiredSessionsClients(int expireSessionsAfterSeconds); |
| 103 | }; | 103 | }; |
| 104 | 104 | ||
| 105 | #endif // SUBSCRIPTIONSTORE_H | 105 | #endif // SUBSCRIPTIONSTORE_H |
timer.cpp
| @@ -83,7 +83,7 @@ void Timer::stop() | @@ -83,7 +83,7 @@ void Timer::stop() | ||
| 83 | 83 | ||
| 84 | void Timer::addCallback(std::function<void ()> f, uint64_t interval_ms, const std::string &name) | 84 | void Timer::addCallback(std::function<void ()> f, uint64_t interval_ms, const std::string &name) |
| 85 | { | 85 | { |
| 86 | - logger->logf(LOG_DEBUG, "Adding event '%s' to the timer.", name.c_str()); | 86 | + logger->logf(LOG_DEBUG, "Adding event '%s' to the timer with an interval of %ld ms.", name.c_str(), interval_ms); |
| 87 | 87 | ||
| 88 | CallbackEntry c; | 88 | CallbackEntry c; |
| 89 | c.f = f; | 89 | c.f = f; |