opal-up 0.0.4 → 0.0.5

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -18,14 +18,14 @@
18
18
  #ifndef UWS_HTTPROUTER_HPP
19
19
  #define UWS_HTTPROUTER_HPP
20
20
 
21
- #include <map>
22
- #include <vector>
23
- #include <cstring>
24
- #include <string_view>
25
- #include <string>
26
21
  #include <algorithm>
22
+ #include <cstring>
23
+ #include <map>
27
24
  #include <memory>
25
+ #include <string>
26
+ #include <string_view>
28
27
  #include <utility>
28
+ #include <vector>
29
29
 
30
30
  #include <iostream>
31
31
 
@@ -33,348 +33,366 @@
33
33
 
34
34
  namespace uWS {
35
35
 
36
- template <class USERDATA>
37
- struct HttpRouter {
38
- static constexpr std::string_view ANY_METHOD_TOKEN = "*";
39
- static const uint32_t HIGH_PRIORITY = 0xd0000000, MEDIUM_PRIORITY = 0xe0000000, LOW_PRIORITY = 0xf0000000;
36
+ template <class USERDATA> struct HttpRouter {
37
+ static constexpr std::string_view ANY_METHOD_TOKEN = "*";
38
+ static const uint32_t HIGH_PRIORITY = 0xd0000000,
39
+ MEDIUM_PRIORITY = 0xe0000000, LOW_PRIORITY = 0xf0000000;
40
40
 
41
41
  private:
42
- USERDATA userData;
43
- static const unsigned int MAX_URL_SEGMENTS = 100;
44
-
45
- /* Handler ids are 32-bit */
46
- static const uint32_t HANDLER_MASK = 0x0fffffff;
47
-
48
- /* List of handlers */
49
- std::vector<MoveOnlyFunction<bool(HttpRouter *)>> handlers;
50
-
51
- /* Current URL cache */
52
- std::string_view currentUrl;
53
- std::string_view urlSegmentVector[MAX_URL_SEGMENTS];
54
- int urlSegmentTop;
55
-
56
- /* The matching tree */
57
- struct Node {
58
- std::string name;
59
- std::vector<std::unique_ptr<Node>> children;
60
- std::vector<uint32_t> handlers;
61
- bool isHighPriority;
62
-
63
- Node(std::string name) : name(name) {}
64
- } root = {"rootNode"};
65
-
66
- /* Sort wildcards after alphanum */
67
- int lexicalOrder(std::string &name) {
68
- if (!name.length()) {
69
- return 2;
70
- }
71
- if (name[0] == ':') {
72
- return 1;
73
- }
74
- if (name[0] == '*') {
75
- return 0;
76
- }
77
- return 2;
42
+ USERDATA userData;
43
+ static const unsigned int MAX_URL_SEGMENTS = 100;
44
+
45
+ /* Handler ids are 32-bit */
46
+ static const uint32_t HANDLER_MASK = 0x0fffffff;
47
+
48
+ /* List of handlers */
49
+ std::vector<MoveOnlyFunction<bool(HttpRouter *)>> handlers;
50
+
51
+ /* Current URL cache */
52
+ std::string_view currentUrl;
53
+ std::string_view urlSegmentVector[MAX_URL_SEGMENTS];
54
+ int urlSegmentTop;
55
+
56
+ /* The matching tree */
57
+ struct Node {
58
+ std::string name;
59
+ std::vector<std::unique_ptr<Node>> children;
60
+ std::vector<uint32_t> handlers;
61
+ bool isHighPriority;
62
+
63
+ Node(std::string name) : name(name) {}
64
+ } root = {"rootNode"};
65
+
66
+ /* Sort wildcards after alphanum */
67
+ int lexicalOrder(std::string &name) {
68
+ if (!name.length()) {
69
+ return 2;
78
70
  }
79
-
80
- /* Advance from parent to child, adding child if necessary */
81
- Node *getNode(Node *parent, std::string child, bool isHighPriority) {
82
- for (std::unique_ptr<Node> &node : parent->children) {
83
- if (node->name == child && node->isHighPriority == isHighPriority) {
84
- return node.get();
85
- }
86
- }
87
-
88
- /* Insert sorted, but keep order if parent is root (we sort methods by priority elsewhere) */
89
- std::unique_ptr<Node> newNode(new Node(child));
90
- newNode->isHighPriority = isHighPriority;
91
- return parent->children.emplace(std::upper_bound(parent->children.begin(), parent->children.end(), newNode, [parent, this](auto &a, auto &b) {
92
-
93
- if (a->isHighPriority != b->isHighPriority) {
94
- return a->isHighPriority;
95
- }
96
-
97
- return b->name.length() && (parent != &root) && (lexicalOrder(b->name) < lexicalOrder(a->name));
98
- }), std::move(newNode))->get();
71
+ if (name[0] == ':') {
72
+ return 1;
99
73
  }
100
-
101
- /* Basically a pre-allocated stack */
102
- struct RouteParameters {
103
- friend struct HttpRouter;
104
- private:
105
- std::string_view params[MAX_URL_SEGMENTS];
106
- int paramsTop;
107
-
108
- void reset() {
109
- paramsTop = -1;
110
- }
111
-
112
- void push(std::string_view param) {
113
- /* We check these bounds indirectly via the urlSegments limit */
114
- params[++paramsTop] = param;
115
- }
116
-
117
- void pop() {
118
- /* Same here, we cannot pop outside */
119
- paramsTop--;
120
- }
121
- } routeParameters;
122
-
123
- /* Set URL for router. Will reset any URL cache */
124
- inline void setUrl(std::string_view url) {
125
-
126
- /* Todo: URL may also start with "http://domain/" or "*", not only "/" */
127
-
128
- /* We expect to stand on a slash */
129
- currentUrl = url;
130
- urlSegmentTop = -1;
74
+ if (name[0] == '*') {
75
+ return 0;
76
+ }
77
+ return 2;
78
+ }
79
+
80
+ /* Advance from parent to child, adding child if necessary */
81
+ Node *getNode(Node *parent, std::string child, bool isHighPriority) {
82
+ for (std::unique_ptr<Node> &node : parent->children) {
83
+ if (node->name == child && node->isHighPriority == isHighPriority) {
84
+ return node.get();
85
+ }
131
86
  }
132
87
 
133
- /* Lazily parse or read from cache */
134
- inline std::pair<std::string_view, bool> getUrlSegment(int urlSegment) {
135
- if (urlSegment > urlSegmentTop) {
136
- /* Signal as STOP when we have no more URL or stack space */
137
- if (!currentUrl.length() || urlSegment > int(MAX_URL_SEGMENTS - 1)) {
138
- return {{}, true};
139
- }
140
-
141
- /* We always stand on a slash here, so step over it */
142
- currentUrl.remove_prefix(1);
88
+ /* Insert sorted, but keep order if parent is root (we sort methods by
89
+ * priority elsewhere) */
90
+ std::unique_ptr<Node> newNode(new Node(child));
91
+ newNode->isHighPriority = isHighPriority;
92
+ return parent->children
93
+ .emplace(std::upper_bound(
94
+ parent->children.begin(), parent->children.end(), newNode,
95
+ [parent, this](auto &a, auto &b) {
96
+ if (a->isHighPriority != b->isHighPriority) {
97
+ return a->isHighPriority;
98
+ }
99
+
100
+ return b->name.length() && (parent != &root) &&
101
+ (lexicalOrder(b->name) < lexicalOrder(a->name));
102
+ }),
103
+ std::move(newNode))
104
+ ->get();
105
+ }
106
+
107
+ /* Basically a pre-allocated stack */
108
+ struct RouteParameters {
109
+ friend struct HttpRouter;
110
+
111
+ private:
112
+ std::string_view params[MAX_URL_SEGMENTS];
113
+ int paramsTop;
114
+
115
+ void reset() { paramsTop = -1; }
116
+
117
+ void push(std::string_view param) {
118
+ /* We check these bounds indirectly via the urlSegments limit */
119
+ params[++paramsTop] = param;
120
+ }
143
121
 
144
- auto segmentLength = currentUrl.find('/');
145
- if (segmentLength == std::string::npos) {
146
- segmentLength = currentUrl.length();
122
+ void pop() {
123
+ /* Same here, we cannot pop outside */
124
+ paramsTop--;
125
+ }
126
+ } routeParameters;
127
+
128
+ /* Set URL for router. Will reset any URL cache */
129
+ inline void setUrl(std::string_view url) {
130
+
131
+ /* Todo: URL may also start with "http://domain/" or "*", not only "/" */
132
+
133
+ /* We expect to stand on a slash */
134
+ currentUrl = url;
135
+ urlSegmentTop = -1;
136
+ }
137
+
138
+ /* Lazily parse or read from cache */
139
+ inline std::pair<std::string_view, bool> getUrlSegment(int urlSegment) {
140
+ if (urlSegment > urlSegmentTop) {
141
+ /* Signal as STOP when we have no more URL or stack space */
142
+ if (!currentUrl.length() || urlSegment > int(MAX_URL_SEGMENTS - 1)) {
143
+ return {{}, true};
144
+ }
145
+
146
+ /* We always stand on a slash here, so step over it */
147
+ currentUrl.remove_prefix(1);
148
+
149
+ auto segmentLength = currentUrl.find('/');
150
+ if (segmentLength == std::string::npos) {
151
+ segmentLength = currentUrl.length();
152
+
153
+ /* Push to url segment vector */
154
+ urlSegmentVector[urlSegment] = currentUrl.substr(0, segmentLength);
155
+ urlSegmentTop++;
156
+
157
+ /* Update currentUrl */
158
+ currentUrl = currentUrl.substr(segmentLength);
159
+ } else {
160
+ /* Push to url segment vector */
161
+ urlSegmentVector[urlSegment] = currentUrl.substr(0, segmentLength);
162
+ urlSegmentTop++;
163
+
164
+ /* Update currentUrl */
165
+ currentUrl = currentUrl.substr(segmentLength);
166
+ }
167
+ }
168
+ /* In any case we return it */
169
+ return {urlSegmentVector[urlSegment], false};
170
+ }
147
171
 
148
- /* Push to url segment vector */
149
- urlSegmentVector[urlSegment] = currentUrl.substr(0, segmentLength);
150
- urlSegmentTop++;
172
+ /* Executes as many handlers it can */
173
+ bool executeHandlers(Node *parent, int urlSegment, USERDATA &userData) {
151
174
 
152
- /* Update currentUrl */
153
- currentUrl = currentUrl.substr(segmentLength);
154
- } else {
155
- /* Push to url segment vector */
156
- urlSegmentVector[urlSegment] = currentUrl.substr(0, segmentLength);
157
- urlSegmentTop++;
175
+ auto [segment, isStop] = getUrlSegment(urlSegment);
158
176
 
159
- /* Update currentUrl */
160
- currentUrl = currentUrl.substr(segmentLength);
161
- }
177
+ /* If we are on STOP, return where we may stand */
178
+ if (isStop) {
179
+ /* We have reached accross the entire URL with no stoppage, execute */
180
+ for (uint32_t handler : parent->handlers) {
181
+ if (handlers[handler & HANDLER_MASK](this)) {
182
+ return true;
162
183
  }
163
- /* In any case we return it */
164
- return {urlSegmentVector[urlSegment], false};
184
+ }
185
+ /* We reached the end, so go back */
186
+ return false;
165
187
  }
166
188
 
167
- /* Executes as many handlers it can */
168
- bool executeHandlers(Node *parent, int urlSegment, USERDATA &userData) {
169
-
170
- auto [segment, isStop] = getUrlSegment(urlSegment);
171
-
172
- /* If we are on STOP, return where we may stand */
173
- if (isStop) {
174
- /* We have reached accross the entire URL with no stoppage, execute */
175
- for (uint32_t handler : parent->handlers) {
176
- if (handlers[handler & HANDLER_MASK](this)) {
177
- return true;
178
- }
179
- }
180
- /* We reached the end, so go back */
181
- return false;
189
+ for (auto &p : parent->children) {
190
+ if (p->name.length() && p->name[0] == '*') {
191
+ /* Wildcard match (can be seen as a shortcut) */
192
+ for (uint32_t handler : p->handlers) {
193
+ if (handlers[handler & HANDLER_MASK](this)) {
194
+ return true;
195
+ }
182
196
  }
183
-
184
- for (auto &p : parent->children) {
185
- if (p->name.length() && p->name[0] == '*') {
186
- /* Wildcard match (can be seen as a shortcut) */
187
- for (uint32_t handler : p->handlers) {
188
- if (handlers[handler & HANDLER_MASK](this)) {
189
- return true;
190
- }
191
- }
192
- } else if (p->name.length() && p->name[0] == ':' && segment.length()) {
193
- /* Parameter match */
194
- routeParameters.push(segment);
195
- if (executeHandlers(p.get(), urlSegment + 1, userData)) {
196
- return true;
197
- }
198
- routeParameters.pop();
199
- } else if (p->name == segment) {
200
- /* Static match */
201
- if (executeHandlers(p.get(), urlSegment + 1, userData)) {
202
- return true;
203
- }
204
- }
197
+ } else if (p->name.length() && p->name[0] == ':' && segment.length()) {
198
+ /* Parameter match */
199
+ routeParameters.push(segment);
200
+ if (executeHandlers(p.get(), urlSegment + 1, userData)) {
201
+ return true;
202
+ }
203
+ routeParameters.pop();
204
+ } else if (p->name == segment) {
205
+ /* Static match */
206
+ if (executeHandlers(p.get(), urlSegment + 1, userData)) {
207
+ return true;
205
208
  }
206
- return false;
209
+ }
207
210
  }
208
-
209
- /* Scans for one matching handler, returning the handler and its priority or UINT32_MAX for not found */
210
- uint32_t findHandler(std::string method, std::string pattern, uint32_t priority) {
211
- for (std::unique_ptr<Node> &node : root.children) {
212
- if (method == node->name) {
213
- setUrl(pattern);
214
- Node *n = node.get();
215
- for (int i = 0; !getUrlSegment(i).second; i++) {
216
- /* Go to next segment or quit */
217
- std::string segment = std::string(getUrlSegment(i).first);
218
- Node *next = nullptr;
219
- for (std::unique_ptr<Node> &child : n->children) {
220
- if (child->name == segment && child->isHighPriority == (priority == HIGH_PRIORITY)) {
221
- next = child.get();
222
- break;
223
- }
224
- }
225
- if (!next) {
226
- return UINT32_MAX;
227
- }
228
- n = next;
229
- }
230
- /* Seek for a priority match in the found node */
231
- for (unsigned int i = 0; i < n->handlers.size(); i++) {
232
- if ((n->handlers[i] & ~HANDLER_MASK) == priority) {
233
- return n->handlers[i];
234
- }
235
- }
236
- return UINT32_MAX;
211
+ return false;
212
+ }
213
+
214
+ /* Scans for one matching handler, returning the handler and its priority or
215
+ * UINT32_MAX for not found */
216
+ uint32_t findHandler(std::string method, std::string pattern,
217
+ uint32_t priority) {
218
+ for (std::unique_ptr<Node> &node : root.children) {
219
+ if (method == node->name) {
220
+ setUrl(pattern);
221
+ Node *n = node.get();
222
+ for (int i = 0; !getUrlSegment(i).second; i++) {
223
+ /* Go to next segment or quit */
224
+ std::string segment = std::string(getUrlSegment(i).first);
225
+ Node *next = nullptr;
226
+ for (std::unique_ptr<Node> &child : n->children) {
227
+ if (child->name == segment &&
228
+ child->isHighPriority == (priority == HIGH_PRIORITY)) {
229
+ next = child.get();
230
+ break;
237
231
  }
232
+ }
233
+ if (!next) {
234
+ return UINT32_MAX;
235
+ }
236
+ n = next;
237
+ }
238
+ /* Seek for a priority match in the found node */
239
+ for (unsigned int i = 0; i < n->handlers.size(); i++) {
240
+ if ((n->handlers[i] & ~HANDLER_MASK) == priority) {
241
+ return n->handlers[i];
242
+ }
238
243
  }
239
244
  return UINT32_MAX;
245
+ }
240
246
  }
247
+ return UINT32_MAX;
248
+ }
241
249
 
242
250
  public:
243
- HttpRouter() {
244
- /* Always have ANY route */
245
- getNode(&root, std::string(ANY_METHOD_TOKEN.data(), ANY_METHOD_TOKEN.length()), false);
246
- }
247
-
248
- std::pair<int, std::string_view *> getParameters() {
249
- return {routeParameters.paramsTop, routeParameters.params};
251
+ HttpRouter() {
252
+ /* Always have ANY route */
253
+ getNode(&root,
254
+ std::string(ANY_METHOD_TOKEN.data(), ANY_METHOD_TOKEN.length()),
255
+ false);
256
+ }
257
+
258
+ std::pair<int, std::string_view *> getParameters() {
259
+ return {routeParameters.paramsTop, routeParameters.params};
260
+ }
261
+
262
+ USERDATA &getUserData() { return userData; }
263
+
264
+ /* Fast path */
265
+ bool route(std::string_view method, std::string_view url) {
266
+ /* Reset url parsing cache */
267
+ setUrl(url);
268
+ routeParameters.reset();
269
+
270
+ /* Begin by finding the method node */
271
+ for (auto &p : root.children) {
272
+ if (p->name == method) {
273
+ /* Then route the url */
274
+ if (executeHandlers(p.get(), 0, userData)) {
275
+ return true;
276
+ } else {
277
+ break;
278
+ }
279
+ }
250
280
  }
251
281
 
252
- USERDATA &getUserData() {
253
- return userData;
282
+ /* Always test any route last */
283
+ return executeHandlers(root.children.back().get(), 0, userData);
284
+ }
285
+
286
+ /* Adds the corresponding entires in matching tree and handler list */
287
+ void add(std::vector<std::string> methods, std::string pattern,
288
+ MoveOnlyFunction<bool(HttpRouter *)> &&handler,
289
+ uint32_t priority = MEDIUM_PRIORITY) {
290
+ /* First remove existing handler */
291
+ remove(methods[0], pattern, priority);
292
+
293
+ for (std::string method : methods) {
294
+ /* Lookup method */
295
+ Node *node = getNode(&root, method, false);
296
+ /* Iterate over all segments */
297
+ setUrl(pattern);
298
+ for (int i = 0; !getUrlSegment(i).second; i++) {
299
+ std::string strippedSegment(getUrlSegment(i).first);
300
+ if (strippedSegment.length() && strippedSegment[0] == ':') {
301
+ /* Parameter routes must be named only : */
302
+ strippedSegment = ":";
303
+ }
304
+ node = getNode(node, strippedSegment, priority == HIGH_PRIORITY);
305
+ }
306
+ /* Insert handler in order sorted by priority (most significant 1 byte) */
307
+ node->handlers.insert(
308
+ std::upper_bound(node->handlers.begin(), node->handlers.end(),
309
+ (uint32_t)(priority | handlers.size())),
310
+ (uint32_t)(priority | handlers.size()));
254
311
  }
255
312
 
256
- /* Fast path */
257
- bool route(std::string_view method, std::string_view url) {
258
- /* Reset url parsing cache */
259
- setUrl(url);
260
- routeParameters.reset();
261
-
262
- /* Begin by finding the method node */
263
- for (auto &p : root.children) {
264
- if (p->name == method) {
265
- /* Then route the url */
266
- if (executeHandlers(p.get(), 0, userData)) {
267
- return true;
313
+ /* Alloate this handler */
314
+ handlers.emplace_back(std::move(handler));
315
+
316
+ /* ANY method must be last, GET must be first */
317
+ std::sort(root.children.begin(), root.children.end(),
318
+ [](const auto &a, const auto &b) {
319
+ /* Assuming the list of methods is unique, non-repeating */
320
+ if (a->name == "GET") {
321
+ return true;
322
+ } else if (b->name == "GET") {
323
+ return false;
324
+ } else if (a->name == ANY_METHOD_TOKEN) {
325
+ return false;
326
+ } else if (b->name == ANY_METHOD_TOKEN) {
327
+ return true;
268
328
  } else {
269
- break;
329
+ return a->name < b->name;
270
330
  }
271
- }
272
- }
273
-
274
- /* Always test any route last */
275
- return executeHandlers(root.children.back().get(), 0, userData);
331
+ });
332
+ }
333
+
334
+ bool cullNode(Node *parent, Node *node, uint32_t handler) {
335
+ /* For all children */
336
+ for (unsigned int i = 0; i < node->children.size();) {
337
+ /* Optimization todo: only enter those with same isHighPrioirty */
338
+ /* Enter child so we get depth first */
339
+ if (!cullNode(node, node->children[i].get(), handler)) {
340
+ /* Only increase if this node was not removed */
341
+ i++;
342
+ }
276
343
  }
277
344
 
278
- /* Adds the corresponding entires in matching tree and handler list */
279
- void add(std::vector<std::string> methods, std::string pattern, MoveOnlyFunction<bool(HttpRouter *)> &&handler, uint32_t priority = MEDIUM_PRIORITY) {
280
- /* First remove existing handler */
281
- remove(methods[0], pattern, priority);
282
-
283
- for (std::string method : methods) {
284
- /* Lookup method */
285
- Node *node = getNode(&root, method, false);
286
- /* Iterate over all segments */
287
- setUrl(pattern);
288
- for (int i = 0; !getUrlSegment(i).second; i++) {
289
- std::string strippedSegment(getUrlSegment(i).first);
290
- if (strippedSegment.length() && strippedSegment[0] == ':') {
291
- /* Parameter routes must be named only : */
292
- strippedSegment = ":";
293
- }
294
- node = getNode(node, strippedSegment, priority == HIGH_PRIORITY);
295
- }
296
- /* Insert handler in order sorted by priority (most significant 1 byte) */
297
- node->handlers.insert(std::upper_bound(node->handlers.begin(), node->handlers.end(), (uint32_t) (priority | handlers.size())), (uint32_t) (priority | handlers.size()));
345
+ /* Cull this node (but skip the root node) */
346
+ if (parent /*&& parent != &root*/) {
347
+ /* Scan for equal (remove), greater (lower by 1) */
348
+ for (auto it = node->handlers.begin(); it != node->handlers.end();) {
349
+ if ((*it & HANDLER_MASK) > (handler & HANDLER_MASK)) {
350
+ *it = ((*it & HANDLER_MASK) - 1) | (*it & ~HANDLER_MASK);
351
+ } else if (*it == handler) {
352
+ it = node->handlers.erase(it);
353
+ continue;
298
354
  }
299
-
300
- /* Alloate this handler */
301
- handlers.emplace_back(std::move(handler));
302
-
303
- /* ANY method must be last, GET must be first */
304
- std::sort(root.children.begin(), root.children.end(), [](const auto &a, const auto &b) {
305
- /* Assuming the list of methods is unique, non-repeating */
306
- if (a->name == "GET") {
307
- return true;
308
- } else if (b->name == "GET") {
309
- return false;
310
- } else if (a->name == ANY_METHOD_TOKEN) {
311
- return false;
312
- } else if (b->name == ANY_METHOD_TOKEN) {
313
- return true;
314
- } else {
315
- return a->name < b->name;
316
- }
317
- });
355
+ it++;
356
+ }
357
+
358
+ /* If we have no children and no handlers, remove us from the
359
+ * parent->children list */
360
+ if (!node->handlers.size() && !node->children.size()) {
361
+ parent->children.erase(
362
+ std::find_if(parent->children.begin(), parent->children.end(),
363
+ [node](const std::unique_ptr<Node> &a) {
364
+ return a.get() == node;
365
+ }));
366
+ /* Returning true means we removed node from parent */
367
+ return true;
368
+ }
318
369
  }
319
370
 
320
- bool cullNode(Node *parent, Node *node, uint32_t handler) {
321
- /* For all children */
322
- for (unsigned int i = 0; i < node->children.size(); ) {
323
- /* Optimization todo: only enter those with same isHighPrioirty */
324
- /* Enter child so we get depth first */
325
- if (!cullNode(node, node->children[i].get(), handler)) {
326
- /* Only increase if this node was not removed */
327
- i++;
328
- }
329
- }
330
-
331
- /* Cull this node (but skip the root node) */
332
- if (parent /*&& parent != &root*/) {
333
- /* Scan for equal (remove), greater (lower by 1) */
334
- for (auto it = node->handlers.begin(); it != node->handlers.end(); ) {
335
- if ((*it & HANDLER_MASK) > (handler & HANDLER_MASK)) {
336
- *it = ((*it & HANDLER_MASK) - 1) | (*it & ~HANDLER_MASK);
337
- } else if (*it == handler) {
338
- it = node->handlers.erase(it);
339
- continue;
340
- }
341
- it++;
342
- }
343
-
344
- /* If we have no children and no handlers, remove us from the parent->children list */
345
- if (!node->handlers.size() && !node->children.size()) {
346
- parent->children.erase(std::find_if(parent->children.begin(), parent->children.end(), [node](const std::unique_ptr<Node> &a) {
347
- return a.get() == node;
348
- }));
349
- /* Returning true means we removed node from parent */
350
- return true;
351
- }
352
- }
353
-
354
- return false;
371
+ return false;
372
+ }
373
+
374
+ /* Removes ALL routes with the same handler as can be found with the given
375
+ * parameters. Removing a wildcard is done by removing ONE OF the methods the
376
+ * wildcard would match with. Example: If wildcard includes POST, GET, PUT,
377
+ * you can remove ALL THREE by removing GET. */
378
+ void remove(std::string method, std::string pattern, uint32_t priority) {
379
+ uint32_t handler = findHandler(method, pattern, priority);
380
+ if (handler == UINT32_MAX) {
381
+ /* Not found or already removed, do nothing */
382
+ return;
355
383
  }
356
384
 
357
- /* Removes ALL routes with the same handler as can be found with the given parameters.
358
- * Removing a wildcard is done by removing ONE OF the methods the wildcard would match with.
359
- * Example: If wildcard includes POST, GET, PUT, you can remove ALL THREE by removing GET. */
360
- void remove(std::string method, std::string pattern, uint32_t priority) {
361
- uint32_t handler = findHandler(method, pattern, priority);
362
- if (handler == UINT32_MAX) {
363
- /* Not found or already removed, do nothing */
364
- return;
365
- }
366
-
367
- /* Cull the entire tree */
368
- /* For all nodes in depth first tree traveral;
369
- * if node contains handler - remove the handler -
370
- * if node holds no handlers after removal, remove the node and return */
371
- cullNode(nullptr, &root, handler);
385
+ /* Cull the entire tree */
386
+ /* For all nodes in depth first tree traveral;
387
+ * if node contains handler - remove the handler -
388
+ * if node holds no handlers after removal, remove the node and return */
389
+ cullNode(nullptr, &root, handler);
372
390
 
373
- /* Now remove the actual handler */
374
- handlers.erase(handlers.begin() + (handler & HANDLER_MASK));
375
- }
391
+ /* Now remove the actual handler */
392
+ handlers.erase(handlers.begin() + (handler & HANDLER_MASK));
393
+ }
376
394
  };
377
395
 
378
- }
396
+ } // namespace uWS
379
397
 
380
398
  #endif // UWS_HTTPROUTER_HPP