opal-up 0.0.4 → 0.0.5

Sign up to get free protection for your applications and to get access to all the features.
data/ext/up_ext/App.h CHANGED
@@ -18,589 +18,710 @@
18
18
  #ifndef UWS_APP_H
19
19
  #define UWS_APP_H
20
20
 
21
- #include <string>
22
21
  #include <charconv>
22
+ #include <string>
23
23
  #include <string_view>
24
24
 
25
25
  namespace uWS {
26
- /* Safari 15.0 - 15.3 has a completely broken compression implementation (client_no_context_takeover not
27
- * properly implemented) - so we fully disable compression for this browser :-(
28
- * see https://github.com/uNetworking/uWebSockets/issues/1347 */
29
- inline bool hasBrokenCompression(std::string_view userAgent) {
30
- size_t posStart = userAgent.find(" Version/15.");
31
- if (posStart == std::string_view::npos) return false;
32
- posStart += 12;
33
-
34
- size_t posEnd = userAgent.find(' ', posStart);
35
- if (posEnd == std::string_view::npos) return false;
36
-
37
- unsigned int minorVersion = 0;
38
- auto result = std::from_chars(userAgent.data() + posStart, userAgent.data() + posEnd, minorVersion);
39
- if (result.ec != std::errc()) return false;
40
- if (result.ptr != userAgent.data() + posEnd) return false; // do not accept trailing chars
41
- if (minorVersion > 3) return false; // we target just Safari 15.0 - 15.3
42
-
43
- if (userAgent.find(" Safari/", posEnd) == std::string_view::npos) return false;
44
-
45
- return true;
46
- }
26
+ /* Safari 15.0 - 15.3 has a completely broken compression implementation
27
+ * (client_no_context_takeover not properly implemented) - so we fully disable
28
+ * compression for this browser :-( see
29
+ * https://github.com/uNetworking/uWebSockets/issues/1347 */
30
+ inline bool hasBrokenCompression(std::string_view userAgent) {
31
+ size_t posStart = userAgent.find(" Version/15.");
32
+ if (posStart == std::string_view::npos)
33
+ return false;
34
+ posStart += 12;
35
+
36
+ size_t posEnd = userAgent.find(' ', posStart);
37
+ if (posEnd == std::string_view::npos)
38
+ return false;
39
+
40
+ unsigned int minorVersion = 0;
41
+ auto result = std::from_chars(userAgent.data() + posStart,
42
+ userAgent.data() + posEnd, minorVersion);
43
+ if (result.ec != std::errc())
44
+ return false;
45
+ if (result.ptr != userAgent.data() + posEnd)
46
+ return false; // do not accept trailing chars
47
+ if (minorVersion > 3)
48
+ return false; // we target just Safari 15.0 - 15.3
49
+
50
+ if (userAgent.find(" Safari/", posEnd) == std::string_view::npos)
51
+ return false;
52
+
53
+ return true;
47
54
  }
55
+ } // namespace uWS
48
56
 
49
- /* An app is a convenience wrapper of some of the most used fuctionalities and allows a
50
- * builder-pattern kind of init. Apps operate on the implicit thread local Loop */
57
+ /* An app is a convenience wrapper of some of the most used fuctionalities and
58
+ * allows a builder-pattern kind of init. Apps operate on the implicit thread
59
+ * local Loop */
51
60
 
52
61
  #include "HttpContext.h"
53
62
  #include "HttpResponse.h"
54
- #include "WebSocketContext.h"
55
- #include "WebSocket.h"
56
63
  #include "PerMessageDeflate.h"
64
+ #include "WebSocket.h"
65
+ #include "WebSocketContext.h"
57
66
 
58
67
  namespace uWS {
59
68
 
60
- /* This one matches us_socket_context_options_t but has default values */
61
- struct SocketContextOptions {
62
- const char *key_file_name = nullptr;
63
- const char *cert_file_name = nullptr;
64
- const char *passphrase = nullptr;
65
- const char *dh_params_file_name = nullptr;
66
- const char *ca_file_name = nullptr;
67
- const char *ssl_ciphers = nullptr;
68
- int ssl_prefer_low_memory_usage = 0;
69
-
70
- /* Conversion operator used internally */
71
- operator struct us_socket_context_options_t() const {
72
- struct us_socket_context_options_t socket_context_options;
73
- memcpy(&socket_context_options, this, sizeof(SocketContextOptions));
74
- return socket_context_options;
75
- }
76
- };
77
-
78
- static_assert(sizeof(struct us_socket_context_options_t) == sizeof(SocketContextOptions), "Mismatching uSockets/uWebSockets ABI");
79
-
80
- template <bool SSL>
81
- struct TemplatedApp {
82
- private:
83
- /* The app always owns at least one http context, but creates websocket contexts on demand */
84
- HttpContext<SSL> *httpContext;
85
- /* WebSocketContexts are of differing type, but we as owners and creators must delete them correctly */
86
- std::vector<MoveOnlyFunction<void()>> webSocketContextDeleters;
87
-
88
- std::vector<void *> webSocketContexts;
89
-
90
- public:
91
-
92
- TopicTree<TopicTreeMessage, TopicTreeBigMessage> *topicTree = nullptr;
93
-
94
- /* Server name */
95
- TemplatedApp &&addServerName(std::string hostname_pattern, SocketContextOptions options = {}) {
96
-
97
- /* Do nothing if not even on SSL */
98
- if constexpr (SSL) {
99
- /* First we create a new router for this domain */
100
- auto *domainRouter = new HttpRouter<typename HttpContextData<SSL>::RouterData>();
101
-
102
- us_socket_context_add_server_name(SSL, (struct us_socket_context_t *) httpContext, hostname_pattern.c_str(), options, domainRouter);
103
- }
104
-
105
- return std::move(*this);
106
- }
107
-
108
- TemplatedApp &&removeServerName(std::string hostname_pattern) {
109
-
110
- /* This will do for now, would be better if us_socket_context_remove_server_name returned the user data */
111
- auto *domainRouter = us_socket_context_find_server_name_userdata(SSL, (struct us_socket_context_t *) httpContext, hostname_pattern.c_str());
112
- if (domainRouter) {
113
- delete (HttpRouter<typename HttpContextData<SSL>::RouterData> *) domainRouter;
114
- }
115
-
116
- us_socket_context_remove_server_name(SSL, (struct us_socket_context_t *) httpContext, hostname_pattern.c_str());
117
- return std::move(*this);
118
- }
119
-
120
- TemplatedApp &&missingServerName(MoveOnlyFunction<void(const char *hostname)> handler) {
121
-
122
- if (!constructorFailed()) {
123
- httpContext->getSocketContextData()->missingServerNameHandler = std::move(handler);
124
-
125
- us_socket_context_on_server_name(SSL, (struct us_socket_context_t *) httpContext, [](struct us_socket_context_t *context, const char *hostname) {
126
-
127
- /* This is the only requirements of being friends with HttpContextData */
128
- HttpContext<SSL> *httpContext = (HttpContext<SSL> *) context;
129
- httpContext->getSocketContextData()->missingServerNameHandler(hostname);
130
- });
131
- }
132
-
133
- return std::move(*this);
134
- }
135
-
136
- /* Returns the SSL_CTX of this app, or nullptr. */
137
- void *getNativeHandle() {
138
- return us_socket_context_get_native_handle(SSL, (struct us_socket_context_t *) httpContext);
139
- }
140
-
141
- /* Attaches a "filter" function to track socket connections/disconnections */
142
- TemplatedApp &&filter(MoveOnlyFunction<void(HttpResponse<SSL> *, int)> &&filterHandler) {
143
- httpContext->filter(std::move(filterHandler));
144
-
145
- return std::move(*this);
146
- }
147
-
148
- /* Publishes a message to all websocket contexts - conceptually as if publishing to the one single
149
- * TopicTree of this app (technically there are many TopicTrees, however the concept is that one
150
- * app has one conceptual Topic tree) */
151
- bool publish(std::string_view topic, std::string_view message, OpCode opCode, bool compress = false) {
152
- /* Anything big bypasses corking efforts */
153
- if (message.length() >= LoopData::CORK_BUFFER_SIZE) {
154
- return topicTree->publishBig(nullptr, topic, {message, opCode, compress}, [](Subscriber *s, TopicTreeBigMessage &message) {
155
- auto *ws = (WebSocket<SSL, true, int> *) s->user;
156
-
157
- /* Send will drain if needed */
158
- ws->send(message.message, (OpCode)message.opCode, message.compress);
159
- });
160
- } else {
161
- return topicTree->publish(nullptr, topic, {std::string(message), opCode, compress});
162
- }
163
- }
69
+ /* This one matches us_socket_context_options_t but has default values */
70
+ struct SocketContextOptions {
71
+ const char *key_file_name = nullptr;
72
+ const char *cert_file_name = nullptr;
73
+ const char *passphrase = nullptr;
74
+ const char *dh_params_file_name = nullptr;
75
+ const char *ca_file_name = nullptr;
76
+ const char *ssl_ciphers = nullptr;
77
+ int ssl_prefer_low_memory_usage = 0;
78
+
79
+ /* Conversion operator used internally */
80
+ operator struct us_socket_context_options_t() const {
81
+ struct us_socket_context_options_t socket_context_options;
82
+ memcpy(&socket_context_options, this, sizeof(SocketContextOptions));
83
+ return socket_context_options;
84
+ }
85
+ };
164
86
 
165
- /* Returns number of subscribers for this topic, or 0 for failure.
166
- * This function should probably be optimized a lot in future releases,
167
- * it could be O(1) with a hash map of fullnames and their counts. */
168
- unsigned int numSubscribers(std::string_view topic) {
169
- Topic *t = topicTree->lookupTopic(topic);
170
- if (t) {
171
- return (unsigned int) t->size();
172
- }
87
+ static_assert(sizeof(struct us_socket_context_options_t) ==
88
+ sizeof(SocketContextOptions),
89
+ "Mismatching uSockets/uWebSockets ABI");
173
90
 
174
- return 0;
175
- }
91
+ template <bool SSL> struct TemplatedApp {
92
+ private:
93
+ /* The app always owns at least one http context, but creates websocket
94
+ * contexts on demand */
95
+ HttpContext<SSL> *httpContext;
96
+ /* WebSocketContexts are of differing type, but we as owners and creators must
97
+ * delete them correctly */
98
+ std::vector<MoveOnlyFunction<void()>> webSocketContextDeleters;
176
99
 
177
- ~TemplatedApp() {
178
- /* Let's just put everything here */
179
- if (httpContext) {
180
- httpContext->free();
100
+ std::vector<void *> webSocketContexts;
181
101
 
182
- /* Free all our webSocketContexts in a type less way */
183
- for (auto &webSocketContextDeleter : webSocketContextDeleters) {
184
- webSocketContextDeleter();
102
+ public:
103
+ TopicTree<TopicTreeMessage, TopicTreeBigMessage> *topicTree = nullptr;
104
+
105
+ /* Server name */
106
+ TemplatedApp &&addServerName(std::string hostname_pattern,
107
+ SocketContextOptions options = {}) {
108
+
109
+ /* Do nothing if not even on SSL */
110
+ if constexpr (SSL) {
111
+ /* First we create a new router for this domain */
112
+ auto *domainRouter =
113
+ new HttpRouter<typename HttpContextData<SSL>::RouterData>();
114
+
115
+ us_socket_context_add_server_name(
116
+ SSL, (struct us_socket_context_t *)httpContext,
117
+ hostname_pattern.c_str(), options, domainRouter);
118
+ }
119
+
120
+ return std::move(*this);
121
+ }
122
+
123
+ TemplatedApp &&removeServerName(std::string hostname_pattern) {
124
+
125
+ /* This will do for now, would be better if
126
+ * us_socket_context_remove_server_name returned the user data */
127
+ auto *domainRouter = us_socket_context_find_server_name_userdata(
128
+ SSL, (struct us_socket_context_t *)httpContext,
129
+ hostname_pattern.c_str());
130
+ if (domainRouter) {
131
+ delete (
132
+ HttpRouter<typename HttpContextData<SSL>::RouterData> *)domainRouter;
133
+ }
134
+
135
+ us_socket_context_remove_server_name(
136
+ SSL, (struct us_socket_context_t *)httpContext,
137
+ hostname_pattern.c_str());
138
+ return std::move(*this);
139
+ }
140
+
141
+ TemplatedApp &&
142
+ missingServerName(MoveOnlyFunction<void(const char *hostname)> handler) {
143
+
144
+ if (!constructorFailed()) {
145
+ httpContext->getSocketContextData()->missingServerNameHandler =
146
+ std::move(handler);
147
+
148
+ us_socket_context_on_server_name(
149
+ SSL, (struct us_socket_context_t *)httpContext,
150
+ [](struct us_socket_context_t *context, const char *hostname) {
151
+ /* This is the only requirements of being friends with
152
+ * HttpContextData */
153
+ HttpContext<SSL> *httpContext = (HttpContext<SSL> *)context;
154
+ httpContext->getSocketContextData()->missingServerNameHandler(
155
+ hostname);
156
+ });
157
+ }
158
+
159
+ return std::move(*this);
160
+ }
161
+
162
+ /* Returns the SSL_CTX of this app, or nullptr. */
163
+ void *getNativeHandle() {
164
+ return us_socket_context_get_native_handle(
165
+ SSL, (struct us_socket_context_t *)httpContext);
166
+ }
167
+
168
+ /* Attaches a "filter" function to track socket connections/disconnections */
169
+ TemplatedApp &&
170
+ filter(MoveOnlyFunction<void(HttpResponse<SSL> *, int)> &&filterHandler) {
171
+ httpContext->filter(std::move(filterHandler));
172
+
173
+ return std::move(*this);
174
+ }
175
+
176
+ /* Publishes a message to all websocket contexts - conceptually as if
177
+ * publishing to the one single TopicTree of this app (technically there are
178
+ * many TopicTrees, however the concept is that one app has one conceptual
179
+ * Topic tree) */
180
+ bool publish(std::string_view topic, std::string_view message, OpCode opCode,
181
+ bool compress = false) {
182
+ /* Anything big bypasses corking efforts */
183
+ if (message.length() >= LoopData::CORK_BUFFER_SIZE) {
184
+ return topicTree->publishBig(
185
+ nullptr, topic, {message, opCode, compress},
186
+ [](Subscriber *s, TopicTreeBigMessage &message) {
187
+ auto *ws = (WebSocket<SSL, true, int> *)s->user;
188
+
189
+ /* Send will drain if needed */
190
+ ws->send(message.message, (OpCode)message.opCode, message.compress);
191
+ });
192
+ } else {
193
+ return topicTree->publish(nullptr, topic,
194
+ {std::string(message), opCode, compress});
195
+ }
196
+ }
197
+
198
+ /* Returns number of subscribers for this topic, or 0 for failure.
199
+ * This function should probably be optimized a lot in future releases,
200
+ * it could be O(1) with a hash map of fullnames and their counts. */
201
+ unsigned int numSubscribers(std::string_view topic) {
202
+ Topic *t = topicTree->lookupTopic(topic);
203
+ if (t) {
204
+ return (unsigned int)t->size();
205
+ }
206
+
207
+ return 0;
208
+ }
209
+
210
+ ~TemplatedApp() {
211
+ /* Let's just put everything here */
212
+ if (httpContext) {
213
+ httpContext->free();
214
+
215
+ /* Free all our webSocketContexts in a type less way */
216
+ for (auto &webSocketContextDeleter : webSocketContextDeleters) {
217
+ webSocketContextDeleter();
218
+ }
219
+ }
220
+
221
+ /* Delete TopicTree */
222
+ if (topicTree) {
223
+ delete topicTree;
224
+
225
+ /* And unregister loop callbacks */
226
+ /* We must unregister any loop post handler here */
227
+ Loop::get()->removePostHandler(topicTree);
228
+ Loop::get()->removePreHandler(topicTree);
229
+ }
230
+ }
231
+
232
+ /* Disallow copying, only move */
233
+ TemplatedApp(const TemplatedApp &other) = delete;
234
+
235
+ TemplatedApp(TemplatedApp &&other) {
236
+ /* Move HttpContext */
237
+ httpContext = other.httpContext;
238
+ other.httpContext = nullptr;
239
+
240
+ /* Move webSocketContextDeleters */
241
+ webSocketContextDeleters = std::move(other.webSocketContextDeleters);
242
+
243
+ webSocketContexts = std::move(other.webSocketContexts);
244
+
245
+ /* Move TopicTree */
246
+ topicTree = other.topicTree;
247
+ other.topicTree = nullptr;
248
+ }
249
+
250
+ TemplatedApp(SocketContextOptions options = {}) {
251
+ httpContext = HttpContext<SSL>::create(Loop::get(), options);
252
+
253
+ /* Register default handler for 404 (can be overridden by user) */
254
+ this->any("/*", [](auto *res, auto * /*req*/) {
255
+ res->writeStatus("404 File Not Found");
256
+ res->end("<html><body><h1>File Not Found</h1><hr><i>uWebSockets/20 "
257
+ "Server</i></body></html>");
258
+ });
259
+ }
260
+
261
+ bool constructorFailed() { return !httpContext; }
262
+
263
+ template <typename UserData> struct WebSocketBehavior {
264
+ /* Disabled compression by default - probably a bad default */
265
+ CompressOptions compression = DISABLED;
266
+ /* Maximum message size we can receive */
267
+ unsigned int maxPayloadLength = 16 * 1024;
268
+ /* 2 minutes timeout is good */
269
+ unsigned short idleTimeout = 120;
270
+ /* 64kb backpressure is probably good */
271
+ unsigned int maxBackpressure = 64 * 1024;
272
+ bool closeOnBackpressureLimit = false;
273
+ /* This one depends on kernel timeouts and is a bad default */
274
+ bool resetIdleTimeoutOnSend = false;
275
+ /* A good default, esp. for newcomers */
276
+ bool sendPingsAutomatically = true;
277
+ /* Maximum socket lifetime in minutes before forced closure (defaults to
278
+ * disabled) */
279
+ unsigned short maxLifetime = 0;
280
+ MoveOnlyFunction<void(HttpResponse<SSL> *, HttpRequest *,
281
+ struct us_socket_context_t *)>
282
+ upgrade = nullptr;
283
+ MoveOnlyFunction<void(WebSocket<SSL, true, UserData> *)> open = nullptr;
284
+ MoveOnlyFunction<void(WebSocket<SSL, true, UserData> *, std::string_view,
285
+ OpCode)>
286
+ message = nullptr;
287
+ MoveOnlyFunction<void(WebSocket<SSL, true, UserData> *, std::string_view,
288
+ OpCode)>
289
+ dropped = nullptr;
290
+ MoveOnlyFunction<void(WebSocket<SSL, true, UserData> *)> drain = nullptr;
291
+ MoveOnlyFunction<void(WebSocket<SSL, true, UserData> *, std::string_view)>
292
+ ping = nullptr;
293
+ MoveOnlyFunction<void(WebSocket<SSL, true, UserData> *, std::string_view)>
294
+ pong = nullptr;
295
+ MoveOnlyFunction<void(WebSocket<SSL, true, UserData> *, std::string_view,
296
+ int, int)>
297
+ subscription = nullptr;
298
+ MoveOnlyFunction<void(WebSocket<SSL, true, UserData> *, int,
299
+ std::string_view)>
300
+ close = nullptr;
301
+ };
302
+
303
+ /* Closes all sockets including listen sockets. */
304
+ TemplatedApp &&close() {
305
+ us_socket_context_close(SSL, (struct us_socket_context_t *)httpContext);
306
+ for (void *webSocketContext : webSocketContexts) {
307
+ us_socket_context_close(SSL,
308
+ (struct us_socket_context_t *)webSocketContext);
309
+ }
310
+
311
+ return std::move(*this);
312
+ }
313
+
314
+ template <typename UserData>
315
+ TemplatedApp &&ws(std::string pattern,
316
+ WebSocketBehavior<UserData> &&behavior) {
317
+ /* Don't compile if alignment rules cannot be satisfied */
318
+ static_assert(
319
+ alignof(UserData) <= LIBUS_EXT_ALIGNMENT,
320
+ "µWebSockets cannot satisfy UserData alignment requirements. You need "
321
+ "to recompile µSockets with LIBUS_EXT_ALIGNMENT adjusted accordingly.");
322
+
323
+ if (!httpContext) {
324
+ return std::move(*this);
325
+ }
326
+
327
+ /* Terminate on misleading idleTimeout values */
328
+ if (behavior.idleTimeout && behavior.idleTimeout < 8) {
329
+ std::cerr << "Error: idleTimeout must be either 0 or greater than 8!"
330
+ << std::endl;
331
+ std::terminate();
332
+ }
333
+
334
+ /* Maximum idleTimeout is 16 minutes */
335
+ if (behavior.idleTimeout > 240 * 4) {
336
+ std::cerr << "Error: idleTimeout must not be greater than 960 seconds!"
337
+ << std::endl;
338
+ std::terminate();
339
+ }
340
+
341
+ /* Maximum maxLifetime is 4 hours */
342
+ if (behavior.maxLifetime > 240) {
343
+ std::cerr << "Error: maxLifetime must not be greater than 240 minutes!"
344
+ << std::endl;
345
+ std::terminate();
346
+ }
347
+
348
+ /* If we don't have a TopicTree yet, create one now */
349
+ if (!topicTree) {
350
+
351
+ bool needsUncork = false;
352
+ topicTree = new TopicTree<TopicTreeMessage, TopicTreeBigMessage>(
353
+ [needsUncork](
354
+ Subscriber *s, TopicTreeMessage &message,
355
+ TopicTree<TopicTreeMessage, TopicTreeBigMessage>::IteratorFlags
356
+ flags) mutable {
357
+ /* Subscriber's user is the socket */
358
+ /* Unfortunately we need to cast is to PerSocketData = int
359
+ * since many different WebSocketContexts use the same
360
+ * TopicTree now */
361
+ auto *ws = (WebSocket<SSL, true, int> *)s->user;
362
+
363
+ /* If this is the first message we try and cork */
364
+ if (flags & TopicTree<TopicTreeMessage,
365
+ TopicTreeBigMessage>::IteratorFlags::FIRST) {
366
+ if (ws->canCork() && !ws->isCorked()) {
367
+ ((AsyncSocket<SSL> *)ws)->cork();
368
+ needsUncork = true;
369
+ }
185
370
  }
186
- }
187
-
188
- /* Delete TopicTree */
189
- if (topicTree) {
190
- delete topicTree;
191
-
192
- /* And unregister loop callbacks */
193
- /* We must unregister any loop post handler here */
194
- Loop::get()->removePostHandler(topicTree);
195
- Loop::get()->removePreHandler(topicTree);
196
- }
197
- }
198
-
199
- /* Disallow copying, only move */
200
- TemplatedApp(const TemplatedApp &other) = delete;
201
-
202
- TemplatedApp(TemplatedApp &&other) {
203
- /* Move HttpContext */
204
- httpContext = other.httpContext;
205
- other.httpContext = nullptr;
206
371
 
207
- /* Move webSocketContextDeleters */
208
- webSocketContextDeleters = std::move(other.webSocketContextDeleters);
372
+ /* If we ever overstep maxBackpresure, exit immediately */
373
+ if (WebSocket<SSL, true, int>::SendStatus::DROPPED ==
374
+ ws->send(message.message, (OpCode)message.opCode,
375
+ message.compress)) {
376
+ if (needsUncork) {
377
+ ((AsyncSocket<SSL> *)ws)->uncork();
378
+ needsUncork = false;
379
+ }
380
+ /* Stop draining */
381
+ return true;
382
+ }
209
383
 
210
- webSocketContexts = std::move(other.webSocketContexts);
384
+ /* If this is the last message we uncork if we are corked */
385
+ if (flags & TopicTree<TopicTreeMessage,
386
+ TopicTreeBigMessage>::IteratorFlags::LAST) {
387
+ /* We should not uncork in all cases? */
388
+ if (needsUncork) {
389
+ ((AsyncSocket<SSL> *)ws)->uncork();
390
+ }
391
+ }
211
392
 
212
- /* Move TopicTree */
213
- topicTree = other.topicTree;
214
- other.topicTree = nullptr;
215
- }
393
+ /* Success */
394
+ return false;
395
+ });
216
396
 
217
- TemplatedApp(SocketContextOptions options = {}) {
218
- httpContext = HttpContext<SSL>::create(Loop::get(), options);
397
+ /* And hook it up with the loop */
398
+ /* We empty for both pre and post just to make sure */
399
+ Loop::get()->addPostHandler(topicTree,
400
+ [topicTree = topicTree](Loop * /*loop*/) {
401
+ /* Commit pub/sub batches every loop
402
+ * iteration */
403
+ topicTree->drain();
404
+ });
219
405
 
220
- /* Register default handler for 404 (can be overridden by user) */
221
- this->any("/*", [](auto *res, auto */*req*/) {
222
- res->writeStatus("404 File Not Found");
223
- res->end("<html><body><h1>File Not Found</h1><hr><i>uWebSockets/20 Server</i></body></html>");
224
- });
406
+ Loop::get()->addPreHandler(topicTree,
407
+ [topicTree = topicTree](Loop * /*loop*/) {
408
+ /* Commit pub/sub batches every loop
409
+ * iteration */
410
+ topicTree->drain();
411
+ });
225
412
  }
226
413
 
227
- bool constructorFailed() {
228
- return !httpContext;
229
- }
414
+ /* Every route has its own websocket context with its own behavior and user
415
+ * data type */
416
+ auto *webSocketContext = WebSocketContext<SSL, true, UserData>::create(
417
+ Loop::get(), (us_socket_context_t *)httpContext, topicTree);
230
418
 
231
- template <typename UserData>
232
- struct WebSocketBehavior {
233
- /* Disabled compression by default - probably a bad default */
234
- CompressOptions compression = DISABLED;
235
- /* Maximum message size we can receive */
236
- unsigned int maxPayloadLength = 16 * 1024;
237
- /* 2 minutes timeout is good */
238
- unsigned short idleTimeout = 120;
239
- /* 64kb backpressure is probably good */
240
- unsigned int maxBackpressure = 64 * 1024;
241
- bool closeOnBackpressureLimit = false;
242
- /* This one depends on kernel timeouts and is a bad default */
243
- bool resetIdleTimeoutOnSend = false;
244
- /* A good default, esp. for newcomers */
245
- bool sendPingsAutomatically = true;
246
- /* Maximum socket lifetime in minutes before forced closure (defaults to disabled) */
247
- unsigned short maxLifetime = 0;
248
- MoveOnlyFunction<void(HttpResponse<SSL> *, HttpRequest *, struct us_socket_context_t *)> upgrade = nullptr;
249
- MoveOnlyFunction<void(WebSocket<SSL, true, UserData> *)> open = nullptr;
250
- MoveOnlyFunction<void(WebSocket<SSL, true, UserData> *, std::string_view, OpCode)> message = nullptr;
251
- MoveOnlyFunction<void(WebSocket<SSL, true, UserData> *, std::string_view, OpCode)> dropped = nullptr;
252
- MoveOnlyFunction<void(WebSocket<SSL, true, UserData> *)> drain = nullptr;
253
- MoveOnlyFunction<void(WebSocket<SSL, true, UserData> *, std::string_view)> ping = nullptr;
254
- MoveOnlyFunction<void(WebSocket<SSL, true, UserData> *, std::string_view)> pong = nullptr;
255
- MoveOnlyFunction<void(WebSocket<SSL, true, UserData> *, std::string_view, int, int)> subscription = nullptr;
256
- MoveOnlyFunction<void(WebSocket<SSL, true, UserData> *, int, std::string_view)> close = nullptr;
257
- };
258
-
259
- /* Closes all sockets including listen sockets. */
260
- TemplatedApp &&close() {
261
- us_socket_context_close(SSL, (struct us_socket_context_t *) httpContext);
262
- for (void *webSocketContext : webSocketContexts) {
263
- us_socket_context_close(SSL, (struct us_socket_context_t *) webSocketContext);
264
- }
265
-
266
- return std::move(*this);
267
- }
419
+ /* We need to clear this later on */
420
+ webSocketContextDeleters.push_back(
421
+ [webSocketContext]() { webSocketContext->free(); });
268
422
 
269
- template <typename UserData>
270
- TemplatedApp &&ws(std::string pattern, WebSocketBehavior<UserData> &&behavior) {
271
- /* Don't compile if alignment rules cannot be satisfied */
272
- static_assert(alignof(UserData) <= LIBUS_EXT_ALIGNMENT,
273
- "µWebSockets cannot satisfy UserData alignment requirements. You need to recompile µSockets with LIBUS_EXT_ALIGNMENT adjusted accordingly.");
274
-
275
- if (!httpContext) {
276
- return std::move(*this);
277
- }
278
-
279
- /* Terminate on misleading idleTimeout values */
280
- if (behavior.idleTimeout && behavior.idleTimeout < 8) {
281
- std::cerr << "Error: idleTimeout must be either 0 or greater than 8!" << std::endl;
282
- std::terminate();
283
- }
284
-
285
- /* Maximum idleTimeout is 16 minutes */
286
- if (behavior.idleTimeout > 240 * 4) {
287
- std::cerr << "Error: idleTimeout must not be greater than 960 seconds!" << std::endl;
288
- std::terminate();
289
- }
290
-
291
- /* Maximum maxLifetime is 4 hours */
292
- if (behavior.maxLifetime > 240) {
293
- std::cerr << "Error: maxLifetime must not be greater than 240 minutes!" << std::endl;
294
- std::terminate();
295
- }
296
-
297
- /* If we don't have a TopicTree yet, create one now */
298
- if (!topicTree) {
299
-
300
- bool needsUncork = false;
301
- topicTree = new TopicTree<TopicTreeMessage, TopicTreeBigMessage>([needsUncork](Subscriber *s, TopicTreeMessage &message, TopicTree<TopicTreeMessage, TopicTreeBigMessage>::IteratorFlags flags) mutable {
302
- /* Subscriber's user is the socket */
303
- /* Unfortunately we need to cast is to PerSocketData = int
304
- * since many different WebSocketContexts use the same
305
- * TopicTree now */
306
- auto *ws = (WebSocket<SSL, true, int> *) s->user;
307
-
308
- /* If this is the first message we try and cork */
309
- if (flags & TopicTree<TopicTreeMessage, TopicTreeBigMessage>::IteratorFlags::FIRST) {
310
- if (ws->canCork() && !ws->isCorked()) {
311
- ((AsyncSocket<SSL> *)ws)->cork();
312
- needsUncork = true;
313
- }
314
- }
315
-
316
- /* If we ever overstep maxBackpresure, exit immediately */
317
- if (WebSocket<SSL, true, int>::SendStatus::DROPPED == ws->send(message.message, (OpCode)message.opCode, message.compress)) {
318
- if (needsUncork) {
319
- ((AsyncSocket<SSL> *)ws)->uncork();
320
- needsUncork = false;
321
- }
322
- /* Stop draining */
323
- return true;
324
- }
325
-
326
- /* If this is the last message we uncork if we are corked */
327
- if (flags & TopicTree<TopicTreeMessage, TopicTreeBigMessage>::IteratorFlags::LAST) {
328
- /* We should not uncork in all cases? */
329
- if (needsUncork) {
330
- ((AsyncSocket<SSL> *)ws)->uncork();
331
- }
332
- }
333
-
334
- /* Success */
335
- return false;
336
- });
337
-
338
- /* And hook it up with the loop */
339
- /* We empty for both pre and post just to make sure */
340
- Loop::get()->addPostHandler(topicTree, [topicTree = topicTree](Loop */*loop*/) {
341
- /* Commit pub/sub batches every loop iteration */
342
- topicTree->drain();
343
- });
344
-
345
- Loop::get()->addPreHandler(topicTree, [topicTree = topicTree](Loop */*loop*/) {
346
- /* Commit pub/sub batches every loop iteration */
347
- topicTree->drain();
348
- });
349
- }
350
-
351
- /* Every route has its own websocket context with its own behavior and user data type */
352
- auto *webSocketContext = WebSocketContext<SSL, true, UserData>::create(Loop::get(), (us_socket_context_t *) httpContext, topicTree);
353
-
354
- /* We need to clear this later on */
355
- webSocketContextDeleters.push_back([webSocketContext]() {
356
- webSocketContext->free();
357
- });
423
+ /* We also keep this list for easy closing */
424
+ webSocketContexts.push_back((void *)webSocketContext);
358
425
 
359
- /* We also keep this list for easy closing */
360
- webSocketContexts.push_back((void *)webSocketContext);
361
-
362
- /* Quick fix to disable any compression if set */
426
+ /* Quick fix to disable any compression if set */
363
427
  #ifdef UWS_NO_ZLIB
364
- behavior.compression = DISABLED;
428
+ behavior.compression = DISABLED;
365
429
  #endif
366
430
 
367
- /* If we are the first one to use compression, initialize it */
368
- if (behavior.compression) {
369
- LoopData *loopData = (LoopData *) us_loop_ext(us_socket_context_loop(SSL, webSocketContext->getSocketContext()));
370
-
371
- /* Initialize loop's deflate inflate streams */
372
- if (!loopData->zlibContext) {
373
- loopData->zlibContext = new ZlibContext;
374
- loopData->inflationStream = new InflationStream(CompressOptions::DEDICATED_DECOMPRESSOR);
375
- loopData->deflationStream = new DeflationStream(CompressOptions::DEDICATED_COMPRESSOR);
376
- }
377
- }
378
-
379
- /* Copy all handlers */
380
- webSocketContext->getExt()->openHandler = std::move(behavior.open);
381
- webSocketContext->getExt()->messageHandler = std::move(behavior.message);
382
- webSocketContext->getExt()->droppedHandler = std::move(behavior.dropped);
383
- webSocketContext->getExt()->drainHandler = std::move(behavior.drain);
384
- webSocketContext->getExt()->subscriptionHandler = std::move(behavior.subscription);
385
- webSocketContext->getExt()->closeHandler = std::move([closeHandler = std::move(behavior.close)](WebSocket<SSL, true, UserData> *ws, int code, std::string_view message) mutable {
386
- if (closeHandler) {
387
- closeHandler(ws, code, message);
388
- }
389
-
390
- /* Destruct user data after returning from close handler */
391
- ((UserData *) ws->getUserData())->~UserData();
431
+ /* If we are the first one to use compression, initialize it */
432
+ if (behavior.compression) {
433
+ LoopData *loopData = (LoopData *)us_loop_ext(
434
+ us_socket_context_loop(SSL, webSocketContext->getSocketContext()));
435
+
436
+ /* Initialize loop's deflate inflate streams */
437
+ if (!loopData->zlibContext) {
438
+ loopData->zlibContext = new ZlibContext;
439
+ loopData->inflationStream =
440
+ new InflationStream(CompressOptions::DEDICATED_DECOMPRESSOR);
441
+ loopData->deflationStream =
442
+ new DeflationStream(CompressOptions::DEDICATED_COMPRESSOR);
443
+ }
444
+ }
445
+
446
+ /* Copy all handlers */
447
+ webSocketContext->getExt()->openHandler = std::move(behavior.open);
448
+ webSocketContext->getExt()->messageHandler = std::move(behavior.message);
449
+ webSocketContext->getExt()->droppedHandler = std::move(behavior.dropped);
450
+ webSocketContext->getExt()->drainHandler = std::move(behavior.drain);
451
+ webSocketContext->getExt()->subscriptionHandler =
452
+ std::move(behavior.subscription);
453
+ webSocketContext->getExt()->closeHandler =
454
+ std::move([closeHandler = std::move(behavior.close)](
455
+ WebSocket<SSL, true, UserData> *ws, int code,
456
+ std::string_view message) mutable {
457
+ if (closeHandler) {
458
+ closeHandler(ws, code, message);
459
+ }
460
+
461
+ /* Destruct user data after returning from close handler */
462
+ ((UserData *)ws->getUserData())->~UserData();
392
463
  });
393
- webSocketContext->getExt()->pingHandler = std::move(behavior.ping);
394
- webSocketContext->getExt()->pongHandler = std::move(behavior.pong);
395
-
396
- /* Copy settings */
397
- webSocketContext->getExt()->maxPayloadLength = behavior.maxPayloadLength;
398
- webSocketContext->getExt()->maxBackpressure = behavior.maxBackpressure;
399
- webSocketContext->getExt()->closeOnBackpressureLimit = behavior.closeOnBackpressureLimit;
400
- webSocketContext->getExt()->resetIdleTimeoutOnSend = behavior.resetIdleTimeoutOnSend;
401
- webSocketContext->getExt()->sendPingsAutomatically = behavior.sendPingsAutomatically;
402
- webSocketContext->getExt()->maxLifetime = behavior.maxLifetime;
403
- webSocketContext->getExt()->compression = behavior.compression;
404
-
405
- /* Calculate idleTimeoutCompnents */
406
- webSocketContext->getExt()->calculateIdleTimeoutCompnents(behavior.idleTimeout);
407
-
408
- httpContext->onHttp("GET", pattern, [webSocketContext, behavior = std::move(behavior)](auto *res, auto *req) mutable {
409
-
410
- /* If we have this header set, it's a websocket */
411
- std::string_view secWebSocketKey = req->getHeader("sec-websocket-key");
412
- if (secWebSocketKey.length() == 24) {
413
-
414
- /* Emit upgrade handler */
415
- if (behavior.upgrade) {
416
-
417
- /* Nasty, ugly Safari 15 hack */
418
- if (hasBrokenCompression(req->getHeader("user-agent"))) {
419
- std::string_view secWebSocketExtensions = req->getHeader("sec-websocket-extensions");
420
- memset((void *) secWebSocketExtensions.data(), ' ', secWebSocketExtensions.length());
421
- }
422
-
423
- behavior.upgrade(res, req, (struct us_socket_context_t *) webSocketContext);
424
- } else {
425
- /* Default handler upgrades to WebSocket */
426
- std::string_view secWebSocketProtocol = req->getHeader("sec-websocket-protocol");
427
- std::string_view secWebSocketExtensions = req->getHeader("sec-websocket-extensions");
428
-
429
- /* Safari 15 hack */
430
- if (hasBrokenCompression(req->getHeader("user-agent"))) {
431
- secWebSocketExtensions = "";
432
- }
433
-
434
- res->template upgrade<UserData>({}, secWebSocketKey, secWebSocketProtocol, secWebSocketExtensions, (struct us_socket_context_t *) webSocketContext);
435
- }
436
-
437
- /* We are going to get uncorked by the Http get return */
438
-
439
- /* We do not need to check for any close or shutdown here as we immediately return from get handler */
440
-
464
+ webSocketContext->getExt()->pingHandler = std::move(behavior.ping);
465
+ webSocketContext->getExt()->pongHandler = std::move(behavior.pong);
466
+
467
+ /* Copy settings */
468
+ webSocketContext->getExt()->maxPayloadLength = behavior.maxPayloadLength;
469
+ webSocketContext->getExt()->maxBackpressure = behavior.maxBackpressure;
470
+ webSocketContext->getExt()->closeOnBackpressureLimit =
471
+ behavior.closeOnBackpressureLimit;
472
+ webSocketContext->getExt()->resetIdleTimeoutOnSend =
473
+ behavior.resetIdleTimeoutOnSend;
474
+ webSocketContext->getExt()->sendPingsAutomatically =
475
+ behavior.sendPingsAutomatically;
476
+ webSocketContext->getExt()->maxLifetime = behavior.maxLifetime;
477
+ webSocketContext->getExt()->compression = behavior.compression;
478
+
479
+ /* Calculate idleTimeoutCompnents */
480
+ webSocketContext->getExt()->calculateIdleTimeoutCompnents(
481
+ behavior.idleTimeout);
482
+
483
+ httpContext->onHttp(
484
+ "GET", pattern,
485
+ [webSocketContext, behavior = std::move(behavior)](auto *res,
486
+ auto *req) mutable {
487
+ /* If we have this header set, it's a websocket */
488
+ std::string_view secWebSocketKey =
489
+ req->getHeader("sec-websocket-key");
490
+ if (secWebSocketKey.length() == 24) {
491
+
492
+ /* Emit upgrade handler */
493
+ if (behavior.upgrade) {
494
+
495
+ /* Nasty, ugly Safari 15 hack */
496
+ if (hasBrokenCompression(req->getHeader("user-agent"))) {
497
+ std::string_view secWebSocketExtensions =
498
+ req->getHeader("sec-websocket-extensions");
499
+ memset((void *)secWebSocketExtensions.data(), ' ',
500
+ secWebSocketExtensions.length());
501
+ }
502
+
503
+ behavior.upgrade(res, req,
504
+ (struct us_socket_context_t *)webSocketContext);
441
505
  } else {
442
- /* Tell the router that we did not handle this request */
443
- req->setYield(true);
506
+ /* Default handler upgrades to WebSocket */
507
+ std::string_view secWebSocketProtocol =
508
+ req->getHeader("sec-websocket-protocol");
509
+ std::string_view secWebSocketExtensions =
510
+ req->getHeader("sec-websocket-extensions");
511
+
512
+ /* Safari 15 hack */
513
+ if (hasBrokenCompression(req->getHeader("user-agent"))) {
514
+ secWebSocketExtensions = "";
515
+ }
516
+
517
+ res->template upgrade<UserData>(
518
+ {}, secWebSocketKey, secWebSocketProtocol,
519
+ secWebSocketExtensions,
520
+ (struct us_socket_context_t *)webSocketContext);
444
521
  }
445
- }, true);
446
- return std::move(*this);
447
- }
448
-
449
- /* Browse to a server name, changing the router to this domain */
450
- TemplatedApp &&domain(std::string serverName) {
451
- HttpContextData<SSL> *httpContextData = httpContext->getSocketContextData();
452
-
453
- void *domainRouter = us_socket_context_find_server_name_userdata(SSL, (struct us_socket_context_t *) httpContext, serverName.c_str());
454
- if (domainRouter) {
455
- std::cout << "Browsed to SNI: " << serverName << std::endl;
456
- httpContextData->currentRouter = (decltype(httpContextData->currentRouter)) domainRouter;
457
- } else {
458
- std::cout << "Cannot browse to SNI: " << serverName << std::endl;
459
- httpContextData->currentRouter = &httpContextData->router;
460
- }
461
-
462
- return std::move(*this);
463
- }
464
-
465
- TemplatedApp &&get(std::string pattern, MoveOnlyFunction<void(HttpResponse<SSL> *, HttpRequest *)> &&handler) {
466
- if (httpContext) {
467
- httpContext->onHttp("GET", pattern, std::move(handler));
468
- }
469
- return std::move(*this);
470
- }
471
-
472
- TemplatedApp &&post(std::string pattern, MoveOnlyFunction<void(HttpResponse<SSL> *, HttpRequest *)> &&handler) {
473
- if (httpContext) {
474
- httpContext->onHttp("POST", pattern, std::move(handler));
475
- }
476
- return std::move(*this);
477
- }
478
-
479
- TemplatedApp &&options(std::string pattern, MoveOnlyFunction<void(HttpResponse<SSL> *, HttpRequest *)> &&handler) {
480
- if (httpContext) {
481
- httpContext->onHttp("OPTIONS", pattern, std::move(handler));
482
- }
483
- return std::move(*this);
484
- }
485
-
486
- TemplatedApp &&del(std::string pattern, MoveOnlyFunction<void(HttpResponse<SSL> *, HttpRequest *)> &&handler) {
487
- if (httpContext) {
488
- httpContext->onHttp("DELETE", pattern, std::move(handler));
489
- }
490
- return std::move(*this);
491
- }
492
-
493
- TemplatedApp &&patch(std::string pattern, MoveOnlyFunction<void(HttpResponse<SSL> *, HttpRequest *)> &&handler) {
494
- if (httpContext) {
495
- httpContext->onHttp("PATCH", pattern, std::move(handler));
496
- }
497
- return std::move(*this);
498
- }
499
-
500
- TemplatedApp &&put(std::string pattern, MoveOnlyFunction<void(HttpResponse<SSL> *, HttpRequest *)> &&handler) {
501
- if (httpContext) {
502
- httpContext->onHttp("PUT", pattern, std::move(handler));
503
- }
504
- return std::move(*this);
505
- }
506
-
507
- TemplatedApp &&head(std::string pattern, MoveOnlyFunction<void(HttpResponse<SSL> *, HttpRequest *)> &&handler) {
508
- if (httpContext) {
509
- httpContext->onHttp("HEAD", pattern, std::move(handler));
510
- }
511
- return std::move(*this);
512
- }
513
-
514
- TemplatedApp &&connect(std::string pattern, MoveOnlyFunction<void(HttpResponse<SSL> *, HttpRequest *)> &&handler) {
515
- if (httpContext) {
516
- httpContext->onHttp("CONNECT", pattern, std::move(handler));
517
- }
518
- return std::move(*this);
519
- }
520
-
521
- TemplatedApp &&trace(std::string pattern, MoveOnlyFunction<void(HttpResponse<SSL> *, HttpRequest *)> &&handler) {
522
- if (httpContext) {
523
- httpContext->onHttp("TRACE", pattern, std::move(handler));
524
- }
525
- return std::move(*this);
526
- }
527
-
528
- /* This one catches any method */
529
- TemplatedApp &&any(std::string pattern, MoveOnlyFunction<void(HttpResponse<SSL> *, HttpRequest *)> &&handler) {
530
- if (httpContext) {
531
- httpContext->onHttp("*", pattern, std::move(handler));
532
- }
533
- return std::move(*this);
534
- }
535
-
536
- /* Host, port, callback */
537
- TemplatedApp &&listen(std::string host, int port, MoveOnlyFunction<void(us_listen_socket_t *)> &&handler) {
538
- if (!host.length()) {
539
- return listen(port, std::move(handler));
540
- }
541
- handler(httpContext ? httpContext->listen(host.c_str(), port, 0) : nullptr);
542
- return std::move(*this);
543
- }
544
-
545
- /* Host, port, options, callback */
546
- TemplatedApp &&listen(std::string host, int port, int options, MoveOnlyFunction<void(us_listen_socket_t *)> &&handler) {
547
- if (!host.length()) {
548
- return listen(port, options, std::move(handler));
549
- }
550
- handler(httpContext ? httpContext->listen(host.c_str(), port, options) : nullptr);
551
- return std::move(*this);
552
- }
553
-
554
- /* Port, callback */
555
- TemplatedApp &&listen(int port, MoveOnlyFunction<void(us_listen_socket_t *)> &&handler) {
556
- handler(httpContext ? httpContext->listen(nullptr, port, 0) : nullptr);
557
- return std::move(*this);
558
- }
559
-
560
- /* Port, options, callback */
561
- TemplatedApp &&listen(int port, int options, MoveOnlyFunction<void(us_listen_socket_t *)> &&handler) {
562
- handler(httpContext ? httpContext->listen(nullptr, port, options) : nullptr);
563
- return std::move(*this);
564
- }
565
-
566
- /* options, callback, path to unix domain socket */
567
- TemplatedApp &&listen(int options, MoveOnlyFunction<void(us_listen_socket_t *)> &&handler, std::string path) {
568
- handler(httpContext ? httpContext->listen(path.c_str(), options) : nullptr);
569
- return std::move(*this);
570
- }
571
-
572
- /* callback, path to unix domain socket */
573
- TemplatedApp &&listen(MoveOnlyFunction<void(us_listen_socket_t *)> &&handler, std::string path) {
574
- handler(httpContext ? httpContext->listen(path.c_str(), 0) : nullptr);
575
- return std::move(*this);
576
- }
577
-
578
- /* Register event handler for accepted FD. Can be used together with adoptSocket. */
579
- TemplatedApp &&preOpen(LIBUS_SOCKET_DESCRIPTOR (*handler)(LIBUS_SOCKET_DESCRIPTOR)) {
580
- httpContext->onPreOpen(handler);
581
- return std::move(*this);
582
- }
583
-
584
- /* adopt an externally accepted socket */
585
- TemplatedApp &&adoptSocket(LIBUS_SOCKET_DESCRIPTOR accepted_fd) {
586
- httpContext->adoptAcceptedSocket(accepted_fd);
587
- return std::move(*this);
588
- }
589
-
590
- TemplatedApp &&run() {
591
- uWS::run();
592
- return std::move(*this);
593
- }
594
-
595
- Loop *getLoop() {
596
- return (Loop *) httpContext->getLoop();
597
- }
598
522
 
523
+ /* We are going to get uncorked by the Http get return */
524
+
525
+ /* We do not need to check for any close or shutdown here as we
526
+ * immediately return from get handler */
527
+
528
+ } else {
529
+ /* Tell the router that we did not handle this request */
530
+ req->setYield(true);
531
+ }
532
+ },
533
+ true);
534
+ return std::move(*this);
535
+ }
536
+
537
+ /* Browse to a server name, changing the router to this domain */
538
+ TemplatedApp &&domain(std::string serverName) {
539
+ HttpContextData<SSL> *httpContextData = httpContext->getSocketContextData();
540
+
541
+ void *domainRouter = us_socket_context_find_server_name_userdata(
542
+ SSL, (struct us_socket_context_t *)httpContext, serverName.c_str());
543
+ if (domainRouter) {
544
+ std::cout << "Browsed to SNI: " << serverName << std::endl;
545
+ httpContextData->currentRouter =
546
+ (decltype(httpContextData->currentRouter))domainRouter;
547
+ } else {
548
+ std::cout << "Cannot browse to SNI: " << serverName << std::endl;
549
+ httpContextData->currentRouter = &httpContextData->router;
550
+ }
551
+
552
+ return std::move(*this);
553
+ }
554
+
555
+ TemplatedApp &&
556
+ get(std::string pattern,
557
+ MoveOnlyFunction<void(HttpResponse<SSL> *, HttpRequest *)> &&handler) {
558
+ if (httpContext) {
559
+ httpContext->onHttp("GET", pattern, std::move(handler));
560
+ }
561
+ return std::move(*this);
562
+ }
563
+
564
+ TemplatedApp &&
565
+ post(std::string pattern,
566
+ MoveOnlyFunction<void(HttpResponse<SSL> *, HttpRequest *)> &&handler) {
567
+ if (httpContext) {
568
+ httpContext->onHttp("POST", pattern, std::move(handler));
569
+ }
570
+ return std::move(*this);
571
+ }
572
+
573
+ TemplatedApp &&options(
574
+ std::string pattern,
575
+ MoveOnlyFunction<void(HttpResponse<SSL> *, HttpRequest *)> &&handler) {
576
+ if (httpContext) {
577
+ httpContext->onHttp("OPTIONS", pattern, std::move(handler));
578
+ }
579
+ return std::move(*this);
580
+ }
581
+
582
+ TemplatedApp &&
583
+ del(std::string pattern,
584
+ MoveOnlyFunction<void(HttpResponse<SSL> *, HttpRequest *)> &&handler) {
585
+ if (httpContext) {
586
+ httpContext->onHttp("DELETE", pattern, std::move(handler));
587
+ }
588
+ return std::move(*this);
589
+ }
590
+
591
+ TemplatedApp &&
592
+ patch(std::string pattern,
593
+ MoveOnlyFunction<void(HttpResponse<SSL> *, HttpRequest *)> &&handler) {
594
+ if (httpContext) {
595
+ httpContext->onHttp("PATCH", pattern, std::move(handler));
596
+ }
597
+ return std::move(*this);
598
+ }
599
+
600
+ TemplatedApp &&
601
+ put(std::string pattern,
602
+ MoveOnlyFunction<void(HttpResponse<SSL> *, HttpRequest *)> &&handler) {
603
+ if (httpContext) {
604
+ httpContext->onHttp("PUT", pattern, std::move(handler));
605
+ }
606
+ return std::move(*this);
607
+ }
608
+
609
+ TemplatedApp &&
610
+ head(std::string pattern,
611
+ MoveOnlyFunction<void(HttpResponse<SSL> *, HttpRequest *)> &&handler) {
612
+ if (httpContext) {
613
+ httpContext->onHttp("HEAD", pattern, std::move(handler));
614
+ }
615
+ return std::move(*this);
616
+ }
617
+
618
+ TemplatedApp &&connect(
619
+ std::string pattern,
620
+ MoveOnlyFunction<void(HttpResponse<SSL> *, HttpRequest *)> &&handler) {
621
+ if (httpContext) {
622
+ httpContext->onHttp("CONNECT", pattern, std::move(handler));
623
+ }
624
+ return std::move(*this);
625
+ }
626
+
627
+ TemplatedApp &&
628
+ trace(std::string pattern,
629
+ MoveOnlyFunction<void(HttpResponse<SSL> *, HttpRequest *)> &&handler) {
630
+ if (httpContext) {
631
+ httpContext->onHttp("TRACE", pattern, std::move(handler));
632
+ }
633
+ return std::move(*this);
634
+ }
635
+
636
+ /* This one catches any method */
637
+ TemplatedApp &&
638
+ any(std::string pattern,
639
+ MoveOnlyFunction<void(HttpResponse<SSL> *, HttpRequest *)> &&handler) {
640
+ if (httpContext) {
641
+ httpContext->onHttp("*", pattern, std::move(handler));
642
+ }
643
+ return std::move(*this);
644
+ }
645
+
646
+ /* Host, port, callback */
647
+ TemplatedApp &&
648
+ listen(std::string host, int port,
649
+ MoveOnlyFunction<void(us_listen_socket_t *)> &&handler) {
650
+ if (!host.length()) {
651
+ return listen(port, std::move(handler));
652
+ }
653
+ handler(httpContext ? httpContext->listen(host.c_str(), port, 0) : nullptr);
654
+ return std::move(*this);
655
+ }
656
+
657
+ /* Host, port, options, callback */
658
+ TemplatedApp &&
659
+ listen(std::string host, int port, int options,
660
+ MoveOnlyFunction<void(us_listen_socket_t *)> &&handler) {
661
+ if (!host.length()) {
662
+ return listen(port, options, std::move(handler));
663
+ }
664
+ handler(httpContext ? httpContext->listen(host.c_str(), port, options)
665
+ : nullptr);
666
+ return std::move(*this);
667
+ }
668
+
669
+ /* Port, callback */
670
+ TemplatedApp &&
671
+ listen(int port, MoveOnlyFunction<void(us_listen_socket_t *)> &&handler) {
672
+ handler(httpContext ? httpContext->listen(nullptr, port, 0) : nullptr);
673
+ return std::move(*this);
674
+ }
675
+
676
+ /* Port, options, callback */
677
+ TemplatedApp &&
678
+ listen(int port, int options,
679
+ MoveOnlyFunction<void(us_listen_socket_t *)> &&handler) {
680
+ handler(httpContext ? httpContext->listen(nullptr, port, options)
681
+ : nullptr);
682
+ return std::move(*this);
683
+ }
684
+
685
+ /* options, callback, path to unix domain socket */
686
+ TemplatedApp &&listen(int options,
687
+ MoveOnlyFunction<void(us_listen_socket_t *)> &&handler,
688
+ std::string path) {
689
+ handler(httpContext ? httpContext->listen(path.c_str(), options) : nullptr);
690
+ return std::move(*this);
691
+ }
692
+
693
+ /* callback, path to unix domain socket */
694
+ TemplatedApp &&listen(MoveOnlyFunction<void(us_listen_socket_t *)> &&handler,
695
+ std::string path) {
696
+ handler(httpContext ? httpContext->listen(path.c_str(), 0) : nullptr);
697
+ return std::move(*this);
698
+ }
699
+
700
+ /* Register event handler for accepted FD. Can be used together with
701
+ * adoptSocket. */
702
+ TemplatedApp &&
703
+ preOpen(LIBUS_SOCKET_DESCRIPTOR (*handler)(LIBUS_SOCKET_DESCRIPTOR)) {
704
+ httpContext->onPreOpen(handler);
705
+ return std::move(*this);
706
+ }
707
+
708
+ /* adopt an externally accepted socket */
709
+ TemplatedApp &&adoptSocket(LIBUS_SOCKET_DESCRIPTOR accepted_fd) {
710
+ httpContext->adoptAcceptedSocket(accepted_fd);
711
+ return std::move(*this);
712
+ }
713
+
714
+ TemplatedApp &&run() {
715
+ uWS::run();
716
+ return std::move(*this);
717
+ }
718
+
719
+ Loop *getLoop() { return (Loop *)httpContext->getLoop(); }
599
720
  };
600
721
 
601
722
  typedef TemplatedApp<false> App;
602
723
  typedef TemplatedApp<true> SSLApp;
603
724
 
604
- }
725
+ } // namespace uWS
605
726
 
606
727
  #endif // UWS_APP_H