Commit 421a666b63fe88dca89e62d81c646b352943eab2

Authored by Wiebe Cazemier
1 parent 9cbd0be5

Fix creating invalid subscription tree on unsubscribe

Unsubscribing paths that didn't exist caused creation of null nodes,
which subsequent use of the tree crashed on.
subscriptionstore.cpp
@@ -61,6 +61,20 @@ void SubscriptionNode::removeSubscriber(const std::shared_ptr<Session> &subscrib @@ -61,6 +61,20 @@ void SubscriptionNode::removeSubscriber(const std::shared_ptr<Session> &subscrib
61 } 61 }
62 } 62 }
63 63
  64 +/**
  65 + * @brief SubscriptionNode::getChildren gets children or null pointer. Const, so doesn't default-create node for
  66 + * non-existing children.
  67 + * @param subtopic
  68 + * @return
  69 + */
  70 +SubscriptionNode *SubscriptionNode::getChildren(const std::string &subtopic) const
  71 +{
  72 + auto it = children.find(subtopic);
  73 + if (it != children.end())
  74 + return it->second.get();
  75 + return nullptr;
  76 +}
  77 +
64 78
65 SubscriptionStore::SubscriptionStore() : 79 SubscriptionStore::SubscriptionStore() :
66 root("root"), 80 root("root"),
@@ -122,26 +136,25 @@ void SubscriptionStore::removeSubscription(std::shared_ptr<Client> &client, cons @@ -122,26 +136,25 @@ void SubscriptionStore::removeSubscription(std::shared_ptr<Client> &client, cons
122 RWLockGuard lock_guard(&subscriptionsRwlock); 136 RWLockGuard lock_guard(&subscriptionsRwlock);
123 lock_guard.wrlock(); 137 lock_guard.wrlock();
124 138
125 - // TODO: because it's so similar to adding a subscription, make a function to retrieve the deepest node? 139 + // This code looks like that for addSubscription(), but it's specifically different in that we don't want to default-create non-existing
  140 + // nodes. We need to abort when that happens.
126 SubscriptionNode *deepestNode = &root; 141 SubscriptionNode *deepestNode = &root;
127 for(const std::string &subtopic : subtopics) 142 for(const std::string &subtopic : subtopics)
128 { 143 {
129 - std::unique_ptr<SubscriptionNode> *selectedChildren = nullptr; 144 + SubscriptionNode *selectedChildren = nullptr;
130 145
131 if (subtopic == "#") 146 if (subtopic == "#")
132 - selectedChildren = &deepestNode->childrenPound; 147 + selectedChildren = deepestNode->childrenPound.get();
133 else if (subtopic == "+") 148 else if (subtopic == "+")
134 - selectedChildren = &deepestNode->childrenPlus; 149 + selectedChildren = deepestNode->childrenPlus.get();
135 else 150 else
136 - selectedChildren = &deepestNode->children[subtopic];  
137 -  
138 - std::unique_ptr<SubscriptionNode> &node = *selectedChildren; 151 + selectedChildren = deepestNode->getChildren(subtopic);
139 152
140 - if (!node) 153 + if (!selectedChildren)
141 { 154 {
142 return; 155 return;
143 } 156 }
144 - deepestNode = node.get(); 157 + deepestNode = selectedChildren;
145 } 158 }
146 159
147 assert(deepestNode); 160 assert(deepestNode);
@@ -237,6 +250,10 @@ void SubscriptionStore::publishRecursively(std::vector&lt;std::string&gt;::const_itera @@ -237,6 +250,10 @@ void SubscriptionStore::publishRecursively(std::vector&lt;std::string&gt;::const_itera
237 return; 250 return;
238 } 251 }
239 252
  253 + // Null nodes in the tree shouldn't happen at this point. It points to bugs elsewhere. However, I don't remember why I check the pointer
  254 + // inside the if-block above, instead of the start of the method.
  255 + assert(this_node != nullptr);
  256 +
240 if (this_node->children.empty() && !this_node->childrenPlus && !this_node->childrenPound) 257 if (this_node->children.empty() && !this_node->childrenPlus && !this_node->childrenPound)
241 return; 258 return;
242 259
subscriptionstore.h
@@ -64,6 +64,8 @@ public: @@ -64,6 +64,8 @@ public:
64 std::unique_ptr<SubscriptionNode> childrenPlus; 64 std::unique_ptr<SubscriptionNode> childrenPlus;
65 std::unique_ptr<SubscriptionNode> childrenPound; 65 std::unique_ptr<SubscriptionNode> childrenPound;
66 66
  67 + SubscriptionNode *getChildren(const std::string &subtopic) const;
  68 +
67 int cleanSubscriptions(); 69 int cleanSubscriptions();
68 }; 70 };
69 71