Coverage Report

Created: 2023-06-06 06:17

/src/uWebSockets/src/HttpRouter.h
Line
Count
Source (jump to first uncovered line)
1
/*
2
 * Authored by Alex Hultman, 2018-2020.
3
 * Intellectual property of third-party.
4
5
 * Licensed under the Apache License, Version 2.0 (the "License");
6
 * you may not use this file except in compliance with the License.
7
 * You may obtain a copy of the License at
8
9
 *     http://www.apache.org/licenses/LICENSE-2.0
10
11
 * Unless required by applicable law or agreed to in writing, software
12
 * distributed under the License is distributed on an "AS IS" BASIS,
13
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
14
 * See the License for the specific language governing permissions and
15
 * limitations under the License.
16
 */
17
18
#ifndef UWS_HTTPROUTER_HPP
19
#define UWS_HTTPROUTER_HPP
20
21
#include <map>
22
#include <vector>
23
#include <cstring>
24
#include <string_view>
25
#include <string>
26
#include <algorithm>
27
#include <memory>
28
#include <utility>
29
30
#include <iostream>
31
32
#include "MoveOnlyFunction.h"
33
34
namespace uWS {
35
36
template <class USERDATA>
37
struct HttpRouter {
38
    /* These are public for now */
39
    std::vector<std::string> upperCasedMethods = {"GET", "POST", "HEAD", "PUT", "DELETE", "CONNECT", "OPTIONS", "TRACE", "PATCH"};
40
    static const uint32_t HIGH_PRIORITY = 0xd0000000, MEDIUM_PRIORITY = 0xe0000000, LOW_PRIORITY = 0xf0000000;
41
42
private:
43
    USERDATA userData;
44
    static const unsigned int MAX_URL_SEGMENTS = 100;
45
46
    /* Handler ids are 32-bit */
47
    static const uint32_t HANDLER_MASK = 0x0fffffff;
48
49
    /* Methods and their respective priority */
50
    std::map<std::string, int> priority;
51
52
    /* List of handlers */
53
    std::vector<MoveOnlyFunction<bool(HttpRouter *)>> handlers;
54
55
    /* Current URL cache */
56
    std::string_view currentUrl;
57
    std::string_view urlSegmentVector[MAX_URL_SEGMENTS];
58
    int urlSegmentTop;
59
60
    /* The matching tree */
61
    struct Node {
62
        std::string name;
63
        std::vector<std::unique_ptr<Node>> children;
64
        std::vector<uint32_t> handlers;
65
        bool isHighPriority;
66
67
20
        Node(std::string name) : name(name) {}
68
    } root = {"rootNode"};
69
70
    /* Sort wildcards after alphanum */
71
12
    int lexicalOrder(std::string &name) {
72
12
        if (!name.length()) {
73
0
            return 2;
74
0
        }
75
12
        if (name[0] == ':') {
76
4
            return 1;
77
4
        }
78
8
        if (name[0] == '*') {
79
4
            return 0;
80
4
        }
81
4
        return 2;
82
8
    }
83
84
    /* Advance from parent to child, adding child if necessary */
85
22
    Node *getNode(Node *parent, std::string child, bool isHighPriority) {
86
22
        for (std::unique_ptr<Node> &node : parent->children) {
87
12
            if (node->name == child && node->isHighPriority == isHighPriority) {
88
4
                return node.get();
89
4
            }
90
12
        }
91
92
        /* Insert sorted, but keep order if parent is root (we sort methods by priority elsewhere) */
93
18
        std::unique_ptr<Node> newNode(new Node(child));
94
18
        newNode->isHighPriority = isHighPriority;
95
18
        return parent->children.emplace(std::upper_bound(parent->children.begin(), parent->children.end(), newNode, [parent, this](auto &a, auto &b) {
96
97
8
            if (a->isHighPriority != b->isHighPriority) {
98
0
                return a->isHighPriority;
99
0
            }
100
101
8
            return b->name.length() && (parent != &root) && (lexicalOrder(b->name) < lexicalOrder(a->name));
102
8
        }), std::move(newNode))->get();
103
22
    }
104
105
    /* Basically a pre-allocated stack */
106
    struct RouteParameters {
107
        friend struct HttpRouter;
108
    private:
109
        std::string_view params[MAX_URL_SEGMENTS];
110
        int paramsTop;
111
112
10.0k
        void reset() {
113
10.0k
            paramsTop = -1;
114
10.0k
        }
115
116
8.83k
        void push(std::string_view param) {
117
            /* We check these bounds indirectly via the urlSegments limit */
118
8.83k
            params[++paramsTop] = param;
119
8.83k
        }
120
121
130
        void pop() {
122
            /* Same here, we cannot pop outside */
123
130
            paramsTop--;
124
130
        }
125
    } routeParameters;
126
127
    /* Set URL for router. Will reset any URL cache */
128
10.0k
    inline void setUrl(std::string_view url) {
129
130
        /* Todo: URL may also start with "http://domain/" or "*", not only "/" */
131
132
        /* We expect to stand on a slash */
133
10.0k
        currentUrl = url;
134
10.0k
        urlSegmentTop = -1;
135
10.0k
    }
136
137
    /* Lazily parse or read from cache */
138
23.1k
    inline std::pair<std::string_view, bool> getUrlSegment(int urlSegment) {
139
23.1k
        if (urlSegment > urlSegmentTop) {
140
            /* Signal as STOP when we have no more URL or stack space */
141
22.8k
            if (!currentUrl.length() || urlSegment > 99) {
142
7.67k
                return {{}, true};
143
7.67k
            }
144
145
            /* We always stand on a slash here, so step over it */
146
15.1k
            currentUrl.remove_prefix(1);
147
148
15.1k
            auto segmentLength = currentUrl.find('/');
149
15.1k
            if (segmentLength == std::string::npos) {
150
8.99k
                segmentLength = currentUrl.length();
151
152
                /* Push to url segment vector */
153
8.99k
                urlSegmentVector[urlSegment] = currentUrl.substr(0, segmentLength);
154
8.99k
                urlSegmentTop++;
155
156
                /* Update currentUrl */
157
8.99k
                currentUrl = currentUrl.substr(segmentLength);
158
8.99k
            } else {
159
                /* Push to url segment vector */
160
6.19k
                urlSegmentVector[urlSegment] = currentUrl.substr(0, segmentLength);
161
6.19k
                urlSegmentTop++;
162
163
                /* Update currentUrl */
164
6.19k
                currentUrl = currentUrl.substr(segmentLength);
165
6.19k
            }
166
15.1k
        }
167
        /* In any case we return it */
168
15.5k
        return {urlSegmentVector[urlSegment], false};
169
23.1k
    }
170
171
    /* Executes as many handlers it can */
172
23.1k
    bool executeHandlers(Node *parent, int urlSegment, USERDATA &userData) {
173
174
23.1k
        auto [segment, isStop] = getUrlSegment(urlSegment);
175
176
        /* If we are on STOP, return where we may stand */
177
23.1k
        if (isStop) {
178
            /* We have reached accross the entire URL with no stoppage, execute */
179
7.65k
            for (uint32_t handler : parent->handlers) {
180
7.58k
                if (handlers[handler & HANDLER_MASK](this)) {
181
7.58k
                    return true;
182
7.58k
                }
183
7.58k
            }
184
            /* We reached the end, so go back */
185
77
            return false;
186
7.65k
        }
187
188
18.3k
        for (auto &p : parent->children) {
189
18.3k
            if (p->name.length() && p->name[0] == '*') {
190
                /* Wildcard match (can be seen as a shortcut) */
191
1.65k
                for (uint32_t handler : p->handlers) {
192
1.65k
                    if (handlers[handler & HANDLER_MASK](this)) {
193
1.50k
                        return true;
194
1.50k
                    }
195
1.65k
                }
196
16.6k
            } else if (p->name.length() && p->name[0] == ':' && segment.length()) {
197
                /* Parameter match */
198
8.83k
                routeParameters.push(segment);
199
8.83k
                if (executeHandlers(p.get(), urlSegment + 1, userData)) {
200
8.70k
                    return true;
201
8.70k
                }
202
130
                routeParameters.pop();
203
7.81k
            } else if (p->name == segment) {
204
                /* Static match */
205
5.03k
                if (executeHandlers(p.get(), urlSegment + 1, userData)) {
206
4.73k
                    return true;
207
4.73k
                }
208
5.03k
            }
209
18.3k
        }
210
526
        return false;
211
15.4k
    }
212
213
    /* Scans for one matching handler, returning the handler and its priority or UINT32_MAX for not found */
214
8
    uint32_t findHandler(std::string method, std::string pattern, uint32_t priority) {
215
10
        for (std::unique_ptr<Node> &node : root.children) {
216
10
            if (method == node->name) {
217
8
                setUrl(pattern);
218
8
                Node *n = node.get();
219
22
                for (int i = 0; !getUrlSegment(i).second; i++) {
220
                    /* Go to next segment or quit */
221
14
                    std::string segment = std::string(getUrlSegment(i).first);
222
14
                    Node *next = nullptr;
223
16
                    for (std::unique_ptr<Node> &child : n->children) {
224
16
                        if (child->name == segment && child->isHighPriority == (priority == HIGH_PRIORITY)) {
225
14
                            next = child.get();
226
14
                            break;
227
14
                        }
228
16
                    }
229
14
                    if (!next) {
230
0
                        return UINT32_MAX;
231
0
                    }
232
14
                    n = next;
233
14
                }
234
                /* Seek for a priority match in the found node */
235
8
                for (unsigned int i = 0; i < n->handlers.size(); i++) {
236
8
                    if ((n->handlers[i] & ~HANDLER_MASK) == priority) {
237
8
                        return n->handlers[i];
238
8
                    }
239
8
                }
240
0
                return UINT32_MAX;
241
8
            }
242
10
        }
243
0
        return UINT32_MAX;
244
8
    }
245
246
public:
247
2
    HttpRouter() {
248
2
        int p = 0;
249
18
        for (std::string &method : upperCasedMethods) {
250
18
            priority[method] = p++;
251
18
        }
252
2
    }
253
254
9.23k
    std::pair<int, std::string_view *> getParameters() {
255
9.23k
        return {routeParameters.paramsTop, routeParameters.params};
256
9.23k
    }
257
258
10.0k
    USERDATA &getUserData() {
259
10.0k
        return userData;
260
10.0k
    }
261
262
    /* Fast path */
263
10.0k
    bool route(std::string_view method, std::string_view url) {
264
        /* Reset url parsing cache */
265
10.0k
        setUrl(url);
266
10.0k
        routeParameters.reset();
267
268
        /* Begin by finding the method node */
269
12.2k
        for (auto &p : root.children) {
270
12.2k
            if (p->name == method) {
271
                /* Then route the url */
272
9.24k
                return executeHandlers(p.get(), 0, userData);
273
9.24k
            }
274
12.2k
        }
275
276
        /* We did not find any handler for this method and url */
277
766
        return false;
278
10.0k
    }
279
280
    /* Adds the corresponding entires in matching tree and handler list */
281
8
    void add(std::vector<std::string> methods, std::string pattern, MoveOnlyFunction<bool(HttpRouter *)> &&handler, uint32_t priority = MEDIUM_PRIORITY) {
282
8
        for (std::string method : methods) {
283
            /* Lookup method */
284
8
            Node *node = getNode(&root, method, false);
285
            /* Iterate over all segments */
286
8
            setUrl(pattern);
287
22
            for (int i = 0; !getUrlSegment(i).second; i++) {
288
14
                node = getNode(node, std::string(getUrlSegment(i).first), priority == HIGH_PRIORITY);
289
14
            }
290
            /* Insert handler in order sorted by priority (most significant 1 byte) */
291
8
            node->handlers.insert(std::upper_bound(node->handlers.begin(), node->handlers.end(), (uint32_t) (priority | handlers.size())), (uint32_t) (priority | handlers.size()));
292
8
        }
293
294
        /* Alloate this handler */
295
8
        handlers.emplace_back(std::move(handler));
296
297
        /* Assume can find this handler again */
298
8
        if (((handlers.size() - 1) | priority) != findHandler(methods[0], pattern, priority)) {
299
0
            std::cerr << "Error: Internal routing error" << std::endl;
300
0
            std::abort();
301
0
        }
302
8
    }
303
304
    bool cullNode(Node *parent, Node *node, uint32_t handler) {
305
        /* For all children */
306
        for (unsigned int i = 0; i < node->children.size(); ) {
307
            /* Optimization todo: only enter those with same isHighPrioirty */
308
            /* Enter child so we get depth first */
309
            if (!cullNode(node, node->children[i].get(), handler)) {
310
                /* Only increase if this node was not removed */
311
                i++;
312
            }
313
        }
314
315
        /* Cull this node (but skip the root node) */
316
        if (parent /*&& parent != &root*/) {
317
            /* Scan for equal (remove), greater (lower by 1) */
318
            for (auto it = node->handlers.begin(); it != node->handlers.end(); ) {
319
                if ((*it & HANDLER_MASK) > (handler & HANDLER_MASK)) {
320
                    *it = ((*it & HANDLER_MASK) - 1) | (*it & ~HANDLER_MASK);
321
                } else if (*it == handler) {
322
                    it = node->handlers.erase(it);
323
                    continue;
324
                }
325
                it++;
326
            }
327
328
            /* If we have no children and no handlers, remove us from the parent->children list */
329
            if (!node->handlers.size() && !node->children.size()) {
330
                parent->children.erase(std::find_if(parent->children.begin(), parent->children.end(), [node](const std::unique_ptr<Node> &a) {
331
                    return a.get() == node;
332
                }));
333
                /* Returning true means we removed node from parent */
334
                return true;
335
            }
336
        }
337
338
        return false;
339
    }
340
341
    /* Removes ALL routes with the same handler as can be found with the given parameters.
342
     * Removing a wildcard is done by removing ONE OF the methods the wildcard would match with.
343
     * Example: If wildcard includes POST, GET, PUT, you can remove ALL THREE by removing GET. */
344
    void remove(std::string method, std::string pattern, uint32_t priority) {
345
        uint32_t handler = findHandler(method, pattern, priority);
346
        if (handler == UINT32_MAX) {
347
            /* Not found or already removed, do nothing */
348
            return;
349
        }
350
351
        /* Cull the entire tree */
352
        /* For all nodes in depth first tree traveral;
353
         * if node contains handler - remove the handler -
354
         * if node holds no handlers after removal, remove the node and return */
355
        cullNode(nullptr, &root, handler);
356
357
        /* Now remove the actual handler */
358
        handlers.erase(handlers.begin() + (handler & HANDLER_MASK));
359
    }
360
};
361
362
}
363
364
#endif // UWS_HTTPROUTER_HPP