opal-up 0.0.2 → 0.0.3

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.
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