opal-up 0.0.2 → 0.0.3

Sign up to get free protection for your applications and to get access to all the features.
Files changed (69) hide show
  1. checksums.yaml +4 -4
  2. data/LICENSE +209 -0
  3. data/README.md +81 -28
  4. data/bin/up_ruby +4 -0
  5. data/bin/up_ruby_cluster +4 -0
  6. data/ext/up_ext/App.h +606 -0
  7. data/ext/up_ext/AsyncSocket.h +355 -0
  8. data/ext/up_ext/AsyncSocketData.h +87 -0
  9. data/ext/up_ext/BloomFilter.h +83 -0
  10. data/ext/up_ext/ChunkedEncoding.h +236 -0
  11. data/ext/up_ext/ClientApp.h +36 -0
  12. data/ext/up_ext/HttpContext.h +502 -0
  13. data/ext/up_ext/HttpContextData.h +56 -0
  14. data/ext/up_ext/HttpErrors.h +53 -0
  15. data/ext/up_ext/HttpParser.h +680 -0
  16. data/ext/up_ext/HttpResponse.h +578 -0
  17. data/ext/up_ext/HttpResponseData.h +95 -0
  18. data/ext/up_ext/HttpRouter.h +380 -0
  19. data/ext/up_ext/Loop.h +204 -0
  20. data/ext/up_ext/LoopData.h +112 -0
  21. data/ext/up_ext/MoveOnlyFunction.h +377 -0
  22. data/ext/up_ext/PerMessageDeflate.h +315 -0
  23. data/ext/up_ext/ProxyParser.h +163 -0
  24. data/ext/up_ext/QueryParser.h +120 -0
  25. data/ext/up_ext/TopicTree.h +363 -0
  26. data/ext/up_ext/Utilities.h +66 -0
  27. data/ext/up_ext/WebSocket.h +381 -0
  28. data/ext/up_ext/WebSocketContext.h +434 -0
  29. data/ext/up_ext/WebSocketContextData.h +109 -0
  30. data/ext/up_ext/WebSocketData.h +86 -0
  31. data/ext/up_ext/WebSocketExtensions.h +256 -0
  32. data/ext/up_ext/WebSocketHandshake.h +145 -0
  33. data/ext/up_ext/WebSocketProtocol.h +506 -0
  34. data/ext/up_ext/bsd.c +767 -0
  35. data/ext/up_ext/bsd.h +109 -0
  36. data/ext/up_ext/context.c +524 -0
  37. data/ext/up_ext/epoll_kqueue.c +458 -0
  38. data/ext/up_ext/epoll_kqueue.h +67 -0
  39. data/ext/up_ext/extconf.rb +5 -0
  40. data/ext/up_ext/internal.h +224 -0
  41. data/ext/up_ext/libusockets.h +350 -0
  42. data/ext/up_ext/libuwebsockets.cpp +1374 -0
  43. data/ext/up_ext/libuwebsockets.h +260 -0
  44. data/ext/up_ext/loop.c +386 -0
  45. data/ext/up_ext/loop_data.h +38 -0
  46. data/ext/up_ext/socket.c +231 -0
  47. data/ext/up_ext/up_ext.c +278 -0
  48. data/lib/up/node/rack_env.rb +2 -2
  49. data/lib/up/ruby/cluster_cli.rb +10 -0
  50. data/lib/up/ruby/rack_cluster.rb +26 -0
  51. data/lib/up/ruby/rack_env.rb +97 -0
  52. data/lib/up/ruby/rack_server.rb +26 -0
  53. data/lib/up/ruby/server_cli.rb +10 -0
  54. data/lib/up/u_web_socket/rack_env.rb +1 -1
  55. data/lib/up/version.rb +1 -1
  56. metadata +71 -18
  57. data/.gitignore +0 -5
  58. data/Gemfile +0 -2
  59. data/example_rack_app/Gemfile +0 -3
  60. data/example_rack_app/config.ru +0 -6
  61. data/example_rack_app/rack_app.rb +0 -5
  62. data/example_roda_app/Gemfile +0 -6
  63. data/example_roda_app/config.ru +0 -6
  64. data/example_roda_app/roda_app.rb +0 -37
  65. data/example_sinatra_app/Gemfile +0 -6
  66. data/example_sinatra_app/config.ru +0 -6
  67. data/example_sinatra_app/sinatra_app.rb +0 -7
  68. data/opal-up.gemspec +0 -27
  69. data/up_logo.svg +0 -256
@@ -0,0 +1,380 @@
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
+ static constexpr std::string_view ANY_METHOD_TOKEN = "*";
39
+ static const uint32_t HIGH_PRIORITY = 0xd0000000, MEDIUM_PRIORITY = 0xe0000000, LOW_PRIORITY = 0xf0000000;
40
+
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;
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
+ }
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();
99
+ }
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;
131
+ }
132
+
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);
143
+
144
+ auto segmentLength = currentUrl.find('/');
145
+ if (segmentLength == std::string::npos) {
146
+ segmentLength = currentUrl.length();
147
+
148
+ /* Push to url segment vector */
149
+ urlSegmentVector[urlSegment] = currentUrl.substr(0, segmentLength);
150
+ urlSegmentTop++;
151
+
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++;
158
+
159
+ /* Update currentUrl */
160
+ currentUrl = currentUrl.substr(segmentLength);
161
+ }
162
+ }
163
+ /* In any case we return it */
164
+ return {urlSegmentVector[urlSegment], false};
165
+ }
166
+
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;
182
+ }
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
+ }
205
+ }
206
+ return false;
207
+ }
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;
237
+ }
238
+ }
239
+ return UINT32_MAX;
240
+ }
241
+
242
+ 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};
250
+ }
251
+
252
+ USERDATA &getUserData() {
253
+ return userData;
254
+ }
255
+
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;
268
+ } else {
269
+ break;
270
+ }
271
+ }
272
+ }
273
+
274
+ /* Always test any route last */
275
+ return executeHandlers(root.children.back().get(), 0, userData);
276
+ }
277
+
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()));
298
+ }
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
+ });
318
+ }
319
+
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;
355
+ }
356
+
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);
372
+
373
+ /* Now remove the actual handler */
374
+ handlers.erase(handlers.begin() + (handler & HANDLER_MASK));
375
+ }
376
+ };
377
+
378
+ }
379
+
380
+ #endif // UWS_HTTPROUTER_HPP
data/ext/up_ext/Loop.h ADDED
@@ -0,0 +1,204 @@
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_LOOP_H
19
+ #define UWS_LOOP_H
20
+
21
+ /* The loop is lazily created per-thread and run with run() */
22
+
23
+ #include "LoopData.h"
24
+ #include "libusockets.h"
25
+ #include <iostream>
26
+
27
+ namespace uWS {
28
+ struct Loop {
29
+ private:
30
+ static void wakeupCb(us_loop_t *loop) {
31
+ LoopData *loopData = (LoopData *) us_loop_ext(loop);
32
+
33
+ /* Swap current deferQueue */
34
+ loopData->deferMutex.lock();
35
+ int oldDeferQueue = loopData->currentDeferQueue;
36
+ loopData->currentDeferQueue = (loopData->currentDeferQueue + 1) % 2;
37
+ loopData->deferMutex.unlock();
38
+
39
+ /* Drain the queue */
40
+ for (auto &x : loopData->deferQueues[oldDeferQueue]) {
41
+ x();
42
+ }
43
+ loopData->deferQueues[oldDeferQueue].clear();
44
+ }
45
+
46
+ static void preCb(us_loop_t *loop) {
47
+ LoopData *loopData = (LoopData *) us_loop_ext(loop);
48
+
49
+ for (auto &p : loopData->preHandlers) {
50
+ p.second((Loop *) loop);
51
+ }
52
+ }
53
+
54
+ static void postCb(us_loop_t *loop) {
55
+ LoopData *loopData = (LoopData *) us_loop_ext(loop);
56
+
57
+ for (auto &p : loopData->postHandlers) {
58
+ p.second((Loop *) loop);
59
+ }
60
+
61
+ /* After every event loop iteration, we must not hold the cork buffer */
62
+ if (loopData->corkedSocket) {
63
+ std::cerr << "Error: Cork buffer must not be held across event loop iterations!" << std::endl;
64
+ std::terminate();
65
+ }
66
+ }
67
+
68
+ Loop() = delete;
69
+ ~Loop() = default;
70
+
71
+ Loop *init() {
72
+ new (us_loop_ext((us_loop_t *) this)) LoopData;
73
+ return this;
74
+ }
75
+
76
+ static Loop *create(void *hint) {
77
+ Loop *loop = ((Loop *) us_create_loop(hint, wakeupCb, preCb, postCb, sizeof(LoopData)))->init();
78
+
79
+ /* We also need some timers (should live off the one 4 second timer rather) */
80
+ LoopData *loopData = (LoopData *) us_loop_ext((struct us_loop_t *) loop);
81
+ loopData->dateTimer = us_create_timer((struct us_loop_t *) loop, 1, sizeof(LoopData *));
82
+ memcpy(us_timer_ext(loopData->dateTimer), &loopData, sizeof(LoopData *));
83
+ us_timer_set(loopData->dateTimer, [](struct us_timer_t *t) {
84
+ LoopData *loopData;
85
+ memcpy(&loopData, us_timer_ext(t), sizeof(LoopData *));
86
+ loopData->updateDate();
87
+ }, 1000, 1000);
88
+
89
+ return loop;
90
+ }
91
+
92
+ /* What to do with loops created with existingNativeLoop? */
93
+ struct LoopCleaner {
94
+ ~LoopCleaner() {
95
+ if(loop && cleanMe) {
96
+ loop->free();
97
+ }
98
+ }
99
+ Loop *loop = nullptr;
100
+ bool cleanMe = false;
101
+ };
102
+
103
+ static LoopCleaner &getLazyLoop() {
104
+ static thread_local LoopCleaner lazyLoop;
105
+ return lazyLoop;
106
+ }
107
+
108
+ public:
109
+ /* Lazily initializes a per-thread loop and returns it.
110
+ * Will automatically free all initialized loops at exit. */
111
+ static Loop *get(void *existingNativeLoop = nullptr) {
112
+ if (!getLazyLoop().loop) {
113
+ /* If we are given a native loop pointer we pass that to uSockets and let it deal with it */
114
+ if (existingNativeLoop) {
115
+ /* Todo: here we want to pass the pointer, not a boolean */
116
+ getLazyLoop().loop = create(existingNativeLoop);
117
+ /* We cannot register automatic free here, must be manually done */
118
+ } else {
119
+ getLazyLoop().loop = create(nullptr);
120
+ getLazyLoop().cleanMe = true;
121
+ }
122
+ }
123
+
124
+ return getLazyLoop().loop;
125
+ }
126
+
127
+ /* Freeing the default loop should be done once */
128
+ void free() {
129
+ LoopData *loopData = (LoopData *) us_loop_ext((us_loop_t *) this);
130
+
131
+ /* Stop and free dateTimer first */
132
+ us_timer_close(loopData->dateTimer);
133
+
134
+ loopData->~LoopData();
135
+ /* uSockets will track whether this loop is owned by us or a borrowed alien loop */
136
+ us_loop_free((us_loop_t *) this);
137
+
138
+ /* Reset lazyLoop */
139
+ getLazyLoop().loop = nullptr;
140
+ }
141
+
142
+ void addPostHandler(void *key, MoveOnlyFunction<void(Loop *)> &&handler) {
143
+ LoopData *loopData = (LoopData *) us_loop_ext((us_loop_t *) this);
144
+
145
+ loopData->postHandlers.emplace(key, std::move(handler));
146
+ }
147
+
148
+ /* Bug: what if you remove a handler while iterating them? */
149
+ void removePostHandler(void *key) {
150
+ LoopData *loopData = (LoopData *) us_loop_ext((us_loop_t *) this);
151
+
152
+ loopData->postHandlers.erase(key);
153
+ }
154
+
155
+ void addPreHandler(void *key, MoveOnlyFunction<void(Loop *)> &&handler) {
156
+ LoopData *loopData = (LoopData *) us_loop_ext((us_loop_t *) this);
157
+
158
+ loopData->preHandlers.emplace(key, std::move(handler));
159
+ }
160
+
161
+ /* Bug: what if you remove a handler while iterating them? */
162
+ void removePreHandler(void *key) {
163
+ LoopData *loopData = (LoopData *) us_loop_ext((us_loop_t *) this);
164
+
165
+ loopData->preHandlers.erase(key);
166
+ }
167
+
168
+ /* Defer this callback on Loop's thread of execution */
169
+ void defer(MoveOnlyFunction<void()> &&cb) {
170
+ LoopData *loopData = (LoopData *) us_loop_ext((us_loop_t *) this);
171
+
172
+ //if (std::thread::get_id() == ) // todo: add fast path for same thread id
173
+ loopData->deferMutex.lock();
174
+ loopData->deferQueues[loopData->currentDeferQueue].emplace_back(std::move(cb));
175
+ loopData->deferMutex.unlock();
176
+
177
+ us_wakeup_loop((us_loop_t *) this);
178
+ }
179
+
180
+ /* Actively block and run this loop */
181
+ void run() {
182
+ us_loop_run((us_loop_t *) this);
183
+ }
184
+
185
+ /* Passively integrate with the underlying default loop */
186
+ /* Used to seamlessly integrate with third parties such as Node.js */
187
+ void integrate() {
188
+ us_loop_integrate((us_loop_t *) this);
189
+ }
190
+
191
+ /* Dynamically change this */
192
+ void setSilent(bool silent) {
193
+ ((LoopData *) us_loop_ext((us_loop_t *) this))->noMark = silent;
194
+ }
195
+ };
196
+
197
+ /* Can be called from any thread to run the thread local loop */
198
+ inline void run() {
199
+ Loop::get()->run();
200
+ }
201
+
202
+ }
203
+
204
+ #endif // UWS_LOOP_H
@@ -0,0 +1,112 @@
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_LOOPDATA_H
19
+ #define UWS_LOOPDATA_H
20
+
21
+ #include <thread>
22
+ #include <functional>
23
+ #include <vector>
24
+ #include <mutex>
25
+ #include <map>
26
+ #include <ctime>
27
+ #include <cstdint>
28
+
29
+ #include "PerMessageDeflate.h"
30
+ #include "MoveOnlyFunction.h"
31
+
32
+ struct us_timer_t;
33
+
34
+ namespace uWS {
35
+
36
+ struct Loop;
37
+
38
+ struct alignas(16) LoopData {
39
+ friend struct Loop;
40
+ private:
41
+ std::mutex deferMutex;
42
+ int currentDeferQueue = 0;
43
+ std::vector<MoveOnlyFunction<void()>> deferQueues[2];
44
+
45
+ /* Map from void ptr to handler */
46
+ std::map<void *, MoveOnlyFunction<void(Loop *)>> postHandlers, preHandlers;
47
+
48
+ public:
49
+ LoopData() {
50
+ updateDate();
51
+ }
52
+
53
+ ~LoopData() {
54
+ /* If we have had App.ws called with compression we need to clear this */
55
+ if (zlibContext) {
56
+ delete zlibContext;
57
+ delete inflationStream;
58
+ delete deflationStream;
59
+ }
60
+ delete [] corkBuffer;
61
+ }
62
+
63
+ void updateDate() {
64
+ time_t now = time(0);
65
+ struct tm tstruct = {};
66
+ #ifdef _WIN32
67
+ /* Micro, fucking soft never follows spec. */
68
+ gmtime_s(&tstruct, &now);
69
+ #else
70
+ gmtime_r(&now, &tstruct);
71
+ #endif
72
+ static const char wday_name[][4] = {
73
+ "Sun", "Mon", "Tue", "Wed", "Thu", "Fri", "Sat"
74
+ };
75
+ static const char mon_name[][4] = {
76
+ "Jan", "Feb", "Mar", "Apr", "May", "Jun",
77
+ "Jul", "Aug", "Sep", "Oct", "Nov", "Dec"
78
+ };
79
+ snprintf(date, 32, "%.3s, %.2u %.3s %.4u %.2u:%.2u:%.2u GMT",
80
+ wday_name[tstruct.tm_wday],
81
+ tstruct.tm_mday % 99,
82
+ mon_name[tstruct.tm_mon],
83
+ (1900 + tstruct.tm_year) % 9999,
84
+ tstruct.tm_hour % 99,
85
+ tstruct.tm_min % 99,
86
+ tstruct.tm_sec % 99);
87
+ }
88
+
89
+ char date[32];
90
+
91
+ /* Be silent */
92
+ bool noMark = false;
93
+
94
+ /* Good 16k for SSL perf. */
95
+ static const unsigned int CORK_BUFFER_SIZE = 16 * 1024;
96
+
97
+ /* Cork data */
98
+ char *corkBuffer = new char[CORK_BUFFER_SIZE];
99
+ unsigned int corkOffset = 0;
100
+ void *corkedSocket = nullptr;
101
+
102
+ /* Per message deflate data */
103
+ ZlibContext *zlibContext = nullptr;
104
+ InflationStream *inflationStream = nullptr;
105
+ DeflationStream *deflationStream = nullptr;
106
+
107
+ us_timer_t *dateTimer;
108
+ };
109
+
110
+ }
111
+
112
+ #endif // UWS_LOOPDATA_H