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.
- checksums.yaml +4 -4
- data/ext/up_ext/App.h +665 -544
- data/ext/up_ext/AsyncSocket.h +307 -284
- data/ext/up_ext/AsyncSocketData.h +35 -51
- data/ext/up_ext/BloomFilter.h +37 -42
- data/ext/up_ext/ChunkedEncoding.h +174 -175
- data/ext/up_ext/ClientApp.h +20 -23
- data/ext/up_ext/HttpContext.h +476 -381
- data/ext/up_ext/HttpContextData.h +20 -20
- data/ext/up_ext/HttpErrors.h +14 -10
- data/ext/up_ext/HttpParser.h +631 -563
- data/ext/up_ext/HttpResponse.h +526 -460
- data/ext/up_ext/HttpResponseData.h +59 -55
- data/ext/up_ext/HttpRouter.h +328 -310
- data/ext/up_ext/Loop.h +174 -168
- data/ext/up_ext/LoopData.h +60 -67
- data/ext/up_ext/MoveOnlyFunction.h +71 -80
- data/ext/up_ext/PerMessageDeflate.h +218 -198
- data/ext/up_ext/ProxyParser.h +100 -99
- data/ext/up_ext/QueryParser.h +91 -84
- data/ext/up_ext/TopicTree.h +273 -268
- data/ext/up_ext/Utilities.h +25 -25
- data/ext/up_ext/WebSocket.h +376 -310
- data/ext/up_ext/WebSocketContext.h +487 -372
- data/ext/up_ext/WebSocketContextData.h +74 -62
- data/ext/up_ext/WebSocketData.h +53 -46
- data/ext/up_ext/WebSocketExtensions.h +194 -178
- data/ext/up_ext/WebSocketHandshake.h +115 -110
- data/ext/up_ext/WebSocketProtocol.h +441 -398
- data/ext/up_ext/up_ext.c +43 -5
- data/lib/up/ruby/cluster.rb +29 -6
- data/lib/up/version.rb +1 -1
- metadata +2 -2
data/ext/up_ext/HttpRouter.h
CHANGED
@@ -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
|
-
|
38
|
-
|
39
|
-
|
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
|
-
|
43
|
-
|
44
|
-
|
45
|
-
|
46
|
-
|
47
|
-
|
48
|
-
|
49
|
-
|
50
|
-
|
51
|
-
|
52
|
-
|
53
|
-
|
54
|
-
|
55
|
-
|
56
|
-
|
57
|
-
|
58
|
-
|
59
|
-
|
60
|
-
|
61
|
-
|
62
|
-
|
63
|
-
|
64
|
-
|
65
|
-
|
66
|
-
|
67
|
-
|
68
|
-
|
69
|
-
|
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
|
-
|
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
|
-
|
102
|
-
|
103
|
-
|
104
|
-
|
105
|
-
|
106
|
-
|
107
|
-
|
108
|
-
|
109
|
-
|
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
|
-
/*
|
134
|
-
|
135
|
-
|
136
|
-
|
137
|
-
|
138
|
-
|
139
|
-
|
140
|
-
|
141
|
-
|
142
|
-
|
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
|
-
|
145
|
-
|
146
|
-
|
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
|
-
|
149
|
-
|
150
|
-
urlSegmentTop++;
|
172
|
+
/* Executes as many handlers it can */
|
173
|
+
bool executeHandlers(Node *parent, int urlSegment, USERDATA &userData) {
|
151
174
|
|
152
|
-
|
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
|
-
|
160
|
-
|
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
|
-
|
164
|
-
|
184
|
+
}
|
185
|
+
/* We reached the end, so go back */
|
186
|
+
return false;
|
165
187
|
}
|
166
188
|
|
167
|
-
|
168
|
-
|
169
|
-
|
170
|
-
|
171
|
-
|
172
|
-
|
173
|
-
|
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
|
-
|
185
|
-
|
186
|
-
|
187
|
-
|
188
|
-
|
189
|
-
|
190
|
-
|
191
|
-
|
192
|
-
|
193
|
-
|
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
|
-
|
209
|
+
}
|
207
210
|
}
|
208
|
-
|
209
|
-
|
210
|
-
|
211
|
-
|
212
|
-
|
213
|
-
|
214
|
-
|
215
|
-
|
216
|
-
|
217
|
-
|
218
|
-
|
219
|
-
|
220
|
-
|
221
|
-
|
222
|
-
|
223
|
-
|
224
|
-
|
225
|
-
|
226
|
-
|
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
|
-
|
244
|
-
|
245
|
-
|
246
|
-
|
247
|
-
|
248
|
-
|
249
|
-
|
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
|
-
|
253
|
-
|
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
|
-
/*
|
257
|
-
|
258
|
-
|
259
|
-
|
260
|
-
|
261
|
-
|
262
|
-
|
263
|
-
|
264
|
-
|
265
|
-
|
266
|
-
|
267
|
-
|
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
|
-
|
329
|
+
return a->name < b->name;
|
270
330
|
}
|
271
|
-
|
272
|
-
|
273
|
-
|
274
|
-
|
275
|
-
|
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
|
-
/*
|
279
|
-
|
280
|
-
|
281
|
-
|
282
|
-
|
283
|
-
|
284
|
-
|
285
|
-
|
286
|
-
|
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
|
-
|
301
|
-
|
302
|
-
|
303
|
-
|
304
|
-
|
305
|
-
|
306
|
-
|
307
|
-
|
308
|
-
|
309
|
-
|
310
|
-
|
311
|
-
|
312
|
-
|
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
|
-
|
321
|
-
|
322
|
-
|
323
|
-
|
324
|
-
|
325
|
-
|
326
|
-
|
327
|
-
|
328
|
-
|
329
|
-
|
330
|
-
|
331
|
-
|
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
|
-
/*
|
358
|
-
|
359
|
-
*
|
360
|
-
|
361
|
-
|
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
|
-
|
374
|
-
|
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
|