Coverage Report

Created: 2023-09-25 07:17

/src/uWebSockets/src/App.h
Line
Count
Source (jump to first uncovered line)
1
/*
2
 * Authored by Alex Hultman, 2018-2020.
3
 * Intellectual property of third-party.
4
5
 * Licensed under the Apache License, Version 2.0 (the "License");
6
 * you may not use this file except in compliance with the License.
7
 * You may obtain a copy of the License at
8
9
 *     http://www.apache.org/licenses/LICENSE-2.0
10
11
 * Unless required by applicable law or agreed to in writing, software
12
 * distributed under the License is distributed on an "AS IS" BASIS,
13
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
14
 * See the License for the specific language governing permissions and
15
 * limitations under the License.
16
 */
17
18
#ifndef UWS_APP_H
19
#define UWS_APP_H
20
21
#include <string>
22
#include <charconv>
23
#include <string_view>
24
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
33.5k
    inline bool hasBrokenCompression(std::string_view userAgent) {
30
33.5k
        size_t posStart = userAgent.find(" Version/15.");
31
33.5k
        if (posStart == std::string_view::npos) return false;
32
7.23k
        posStart += 12;
33
34
7.23k
        size_t posEnd = userAgent.find(' ', posStart);
35
7.23k
        if (posEnd == std::string_view::npos) return false;
36
37
6.81k
        unsigned int minorVersion = 0;
38
6.81k
        auto result = std::from_chars(userAgent.data() + posStart, userAgent.data() + posEnd, minorVersion);
39
6.81k
        if (result.ec != std::errc()) return false;
40
5.65k
        if (result.ptr != userAgent.data() + posEnd) return false; // do not accept trailing chars
41
2.62k
        if (minorVersion > 3) return false; // we target just Safari 15.0 - 15.3
42
43
841
        if (userAgent.find(" Safari/", posEnd) == std::string_view::npos) return false;
44
45
206
        return true;
46
841
    }
47
}
48
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 */
51
52
#include "HttpContext.h"
53
#include "HttpResponse.h"
54
#include "WebSocketContext.h"
55
#include "WebSocket.h"
56
#include "PerMessageDeflate.h"
57
58
namespace uWS {
59
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
6.03k
        operator struct us_socket_context_options_t() const {
72
6.03k
            struct us_socket_context_options_t socket_context_options;
73
6.03k
            memcpy(&socket_context_options, this, sizeof(SocketContextOptions));
74
6.03k
            return socket_context_options;
75
6.03k
        }
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
6.03k
    TemplatedApp &&addServerName(std::string hostname_pattern, SocketContextOptions options = {}) {
96
97
        /* Do nothing if not even on SSL */
98
6.03k
        if constexpr (SSL) {
99
            /* First we create a new router for this domain */
100
6.03k
            auto *domainRouter = new HttpRouter<typename HttpContextData<SSL>::RouterData>();
101
102
6.03k
            us_socket_context_add_server_name(SSL, (struct us_socket_context_t *) httpContext, hostname_pattern.c_str(), options, domainRouter);
103
6.03k
        }
104
105
6.03k
        return std::move(*this);
106
6.03k
    }
107
108
6.03k
    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
6.03k
        auto *domainRouter = us_socket_context_find_server_name_userdata(SSL, (struct us_socket_context_t *) httpContext, hostname_pattern.c_str());
112
6.03k
        if (domainRouter) {
113
0
            delete (HttpRouter<typename HttpContextData<SSL>::RouterData> *) domainRouter;
114
0
        }
115
116
6.03k
        us_socket_context_remove_server_name(SSL, (struct us_socket_context_t *) httpContext, hostname_pattern.c_str());
117
6.03k
        return std::move(*this);
118
6.03k
    }
119
120
6.03k
    TemplatedApp &&missingServerName(MoveOnlyFunction<void(const char *hostname)> handler) {
121
122
6.03k
        if (!constructorFailed()) {
123
6.03k
            httpContext->getSocketContextData()->missingServerNameHandler = std::move(handler);
124
125
6.03k
            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
0
                HttpContext<SSL> *httpContext = (HttpContext<SSL> *) context;
129
0
                httpContext->getSocketContextData()->missingServerNameHandler(hostname);
130
0
            });
131
6.03k
        }
132
133
6.03k
        return std::move(*this);
134
6.03k
    }
135
136
    /* Returns the SSL_CTX of this app, or nullptr. */
137
6.03k
    void *getNativeHandle() {
138
6.03k
        return us_socket_context_get_native_handle(SSL, (struct us_socket_context_t *) httpContext);
139
6.03k
    }
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
    }
164
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
        }
173
174
        return 0;
175
    }
176
177
12.0k
    ~TemplatedApp() {
178
        /* Let's just put everything here */
179
12.0k
        if (httpContext) {
180
6.03k
            httpContext->free();
181
182
            /* Free all our webSocketContexts in a type less way */
183
12.0k
            for (auto &webSocketContextDeleter : webSocketContextDeleters) {
184
12.0k
                webSocketContextDeleter();
185
12.0k
            }
186
6.03k
        }
187
188
        /* Delete TopicTree */
189
12.0k
        if (topicTree) {
190
6.03k
            delete topicTree;
191
192
            /* And unregister loop callbacks */
193
            /* We must unregister any loop post handler here */
194
6.03k
            Loop::get()->removePostHandler(topicTree);
195
6.03k
            Loop::get()->removePreHandler(topicTree);
196
6.03k
        }
197
12.0k
    }
198
199
    /* Disallow copying, only move */
200
    TemplatedApp(const TemplatedApp &other) = delete;
201
202
6.03k
    TemplatedApp(TemplatedApp &&other) {
203
        /* Move HttpContext */
204
6.03k
        httpContext = other.httpContext;
205
6.03k
        other.httpContext = nullptr;
206
207
        /* Move webSocketContextDeleters */
208
6.03k
        webSocketContextDeleters = std::move(other.webSocketContextDeleters);
209
210
6.03k
        webSocketContexts = std::move(other.webSocketContexts);
211
212
        /* Move TopicTree */
213
6.03k
        topicTree = other.topicTree;
214
6.03k
        other.topicTree = nullptr;
215
6.03k
    }
216
217
6.03k
    TemplatedApp(SocketContextOptions options = {}) {
218
6.03k
        httpContext = HttpContext<SSL>::create(Loop::get(), options);
219
6.03k
    }
220
221
6.03k
    bool constructorFailed() {
222
6.03k
        return !httpContext;
223
6.03k
    }
224
225
    template <typename UserData>
226
    struct WebSocketBehavior {
227
        /* Disabled compression by default - probably a bad default */
228
        CompressOptions compression = DISABLED;
229
        /* Maximum message size we can receive */
230
        unsigned int maxPayloadLength = 16 * 1024;
231
        /* 2 minutes timeout is good */
232
        unsigned short idleTimeout = 120;
233
        /* 64kb backpressure is probably good */
234
        unsigned int maxBackpressure = 64 * 1024;
235
        bool closeOnBackpressureLimit = false;
236
        /* This one depends on kernel timeouts and is a bad default */
237
        bool resetIdleTimeoutOnSend = false;
238
        /* A good default, esp. for newcomers */
239
        bool sendPingsAutomatically = true;
240
        /* Maximum socket lifetime in minutes before forced closure (defaults to disabled) */
241
        unsigned short maxLifetime = 0;
242
        MoveOnlyFunction<void(HttpResponse<SSL> *, HttpRequest *, struct us_socket_context_t *)> upgrade = nullptr;
243
        MoveOnlyFunction<void(WebSocket<SSL, true, UserData> *)> open = nullptr;
244
        MoveOnlyFunction<void(WebSocket<SSL, true, UserData> *, std::string_view, OpCode)> message = nullptr;
245
        MoveOnlyFunction<void(WebSocket<SSL, true, UserData> *)> drain = nullptr;
246
        MoveOnlyFunction<void(WebSocket<SSL, true, UserData> *, std::string_view)> ping = nullptr;
247
        MoveOnlyFunction<void(WebSocket<SSL, true, UserData> *, std::string_view)> pong = nullptr;
248
        MoveOnlyFunction<void(WebSocket<SSL, true, UserData> *, std::string_view, int, int)> subscription = nullptr;
249
        MoveOnlyFunction<void(WebSocket<SSL, true, UserData> *, int, std::string_view)> close = nullptr;
250
    };
251
252
    /* Closes all sockets including listen sockets. */
253
    TemplatedApp &&close() {
254
        us_socket_context_close(SSL, (struct us_socket_context_t *) httpContext);
255
        for (void *webSocketContext : webSocketContexts) {
256
            us_socket_context_close(SSL, (struct us_socket_context_t *) webSocketContext);
257
        }
258
259
        return std::move(*this);
260
    }
261
262
    template <typename UserData>
263
12.0k
    TemplatedApp &&ws(std::string pattern, WebSocketBehavior<UserData> &&behavior) {
264
        /* Don't compile if alignment rules cannot be satisfied */
265
12.0k
        static_assert(alignof(UserData) <= LIBUS_EXT_ALIGNMENT,
266
12.0k
        "µWebSockets cannot satisfy UserData alignment requirements. You need to recompile µSockets with LIBUS_EXT_ALIGNMENT adjusted accordingly.");
267
268
12.0k
        if (!httpContext) {
269
0
            return std::move(*this);
270
0
        }
271
272
        /* Terminate on misleading idleTimeout values */
273
12.0k
        if (behavior.idleTimeout && behavior.idleTimeout < 8) {
274
0
            std::cerr << "Error: idleTimeout must be either 0 or greater than 8!" << std::endl;
275
0
            std::terminate();
276
0
        }
277
278
        /* Maximum idleTimeout is 16 minutes */
279
12.0k
        if (behavior.idleTimeout > 240 * 4) {
280
0
            std::cerr << "Error: idleTimeout must not be greater than 960 seconds!" << std::endl;
281
0
            std::terminate();
282
0
        }
283
284
        /* Maximum maxLifetime is 4 hours */
285
12.0k
        if (behavior.maxLifetime > 240) {
286
0
            std::cerr << "Error: maxLifetime must not be greater than 240 minutes!" << std::endl;
287
0
            std::terminate();
288
0
        }
289
290
        /* If we don't have a TopicTree yet, create one now */
291
12.0k
        if (!topicTree) {
292
293
6.03k
            bool needsUncork = false;
294
6.03k
            topicTree = new TopicTree<TopicTreeMessage, TopicTreeBigMessage>([needsUncork](Subscriber *s, TopicTreeMessage &message, TopicTree<TopicTreeMessage, TopicTreeBigMessage>::IteratorFlags flags) mutable {
295
                /* Subscriber's user is the socket */
296
                /* Unfortunately we need to cast is to PerSocketData = int
297
                 * since many different WebSocketContexts use the same
298
                 * TopicTree now */
299
0
                auto *ws = (WebSocket<SSL, true, int> *) s->user;
300
301
                /* If this is the first message we try and cork */
302
0
                if (flags & TopicTree<TopicTreeMessage, TopicTreeBigMessage>::IteratorFlags::FIRST) {
303
0
                    if (ws->canCork() && !ws->isCorked()) {
304
0
                        ((AsyncSocket<SSL> *)ws)->cork();
305
0
                        needsUncork = true;
306
0
                    }
307
0
                }
308
309
                /* If we ever overstep maxBackpresure, exit immediately */
310
0
                if (WebSocket<SSL, true, int>::SendStatus::DROPPED == ws->send(message.message, (OpCode)message.opCode, message.compress)) {
311
0
                    if (needsUncork) {
312
0
                        ((AsyncSocket<SSL> *)ws)->uncork();
313
0
                        needsUncork = false;
314
0
                    }
315
                    /* Stop draining */
316
0
                    return true;
317
0
                }
318
319
                /* If this is the last message we uncork if we are corked */
320
0
                if (flags & TopicTree<TopicTreeMessage, TopicTreeBigMessage>::IteratorFlags::LAST) {
321
                    /* We should not uncork in all cases? */
322
0
                    if (needsUncork) {
323
0
                        ((AsyncSocket<SSL> *)ws)->uncork();
324
0
                    }
325
0
                }
326
327
                /* Success */
328
0
                return false;
329
0
            });
330
331
            /* And hook it up with the loop */
332
            /* We empty for both pre and post just to make sure */
333
1.45M
            Loop::get()->addPostHandler(topicTree, [topicTree = topicTree](Loop */*loop*/) {
334
                /* Commit pub/sub batches every loop iteration */
335
1.45M
                topicTree->drain();
336
1.45M
            });
337
338
1.45M
            Loop::get()->addPreHandler(topicTree, [topicTree = topicTree](Loop */*loop*/) {
339
                /* Commit pub/sub batches every loop iteration */
340
1.45M
                topicTree->drain();
341
1.45M
            });
342
6.03k
        }
343
344
        /* Every route has its own websocket context with its own behavior and user data type */
345
12.0k
        auto *webSocketContext = WebSocketContext<SSL, true, UserData>::create(Loop::get(), (us_socket_context_t *) httpContext, topicTree);
346
347
        /* We need to clear this later on */
348
12.0k
        webSocketContextDeleters.push_back([webSocketContext]() {
349
12.0k
            webSocketContext->free();
350
12.0k
        });
351
352
        /* We also keep this list for easy closing */
353
12.0k
        webSocketContexts.push_back((void *)webSocketContext);
354
355
        /* Quick fix to disable any compression if set */
356
#ifdef UWS_NO_ZLIB
357
        behavior.compression = DISABLED;
358
#endif
359
360
        /* If we are the first one to use compression, initialize it */
361
12.0k
        if (behavior.compression) {
362
6.03k
            LoopData *loopData = (LoopData *) us_loop_ext(us_socket_context_loop(SSL, webSocketContext->getSocketContext()));
363
364
            /* Initialize loop's deflate inflate streams */
365
6.03k
            if (!loopData->zlibContext) {
366
6.03k
                loopData->zlibContext = new ZlibContext;
367
6.03k
                loopData->inflationStream = new InflationStream(CompressOptions::DEDICATED_DECOMPRESSOR);
368
6.03k
                loopData->deflationStream = new DeflationStream(CompressOptions::DEDICATED_COMPRESSOR);
369
6.03k
            }
370
6.03k
        }
371
372
        /* Copy all handlers */
373
12.0k
        webSocketContext->getExt()->openHandler = std::move(behavior.open);
374
12.0k
        webSocketContext->getExt()->messageHandler = std::move(behavior.message);
375
12.0k
        webSocketContext->getExt()->drainHandler = std::move(behavior.drain);
376
12.0k
        webSocketContext->getExt()->subscriptionHandler = std::move(behavior.subscription);
377
33.5k
        webSocketContext->getExt()->closeHandler = std::move([closeHandler = std::move(behavior.close)](WebSocket<SSL, true, UserData> *ws, int code, std::string_view message) mutable {
378
33.5k
            if (closeHandler) {
379
32.3k
                closeHandler(ws, code, message);
380
32.3k
            }
381
382
            /* Destruct user data after returning from close handler */
383
33.5k
            ((UserData *) ws->getUserData())->~UserData();
384
33.5k
        });
385
12.0k
        webSocketContext->getExt()->pingHandler = std::move(behavior.ping);
386
12.0k
        webSocketContext->getExt()->pongHandler = std::move(behavior.pong);
387
388
        /* Copy settings */
389
12.0k
        webSocketContext->getExt()->maxPayloadLength = behavior.maxPayloadLength;
390
12.0k
        webSocketContext->getExt()->maxBackpressure = behavior.maxBackpressure;
391
12.0k
        webSocketContext->getExt()->closeOnBackpressureLimit = behavior.closeOnBackpressureLimit;
392
12.0k
        webSocketContext->getExt()->resetIdleTimeoutOnSend = behavior.resetIdleTimeoutOnSend;
393
12.0k
        webSocketContext->getExt()->sendPingsAutomatically = behavior.sendPingsAutomatically;
394
12.0k
        webSocketContext->getExt()->maxLifetime = behavior.maxLifetime;
395
12.0k
        webSocketContext->getExt()->compression = behavior.compression;
396
397
        /* Calculate idleTimeoutCompnents */
398
12.0k
        webSocketContext->getExt()->calculateIdleTimeoutCompnents(behavior.idleTimeout);
399
400
46.0k
        httpContext->onHttp("GET", pattern, [webSocketContext, behavior = std::move(behavior)](auto *res, auto *req) mutable {
401
402
            /* If we have this header set, it's a websocket */
403
46.0k
            std::string_view secWebSocketKey = req->getHeader("sec-websocket-key");
404
46.0k
            if (secWebSocketKey.length() == 24) {
405
406
                /* Emit upgrade handler */
407
33.5k
                if (behavior.upgrade) {
408
409
                    /* Nasty, ugly Safari 15 hack */
410
0
                    if (hasBrokenCompression(req->getHeader("user-agent"))) {
411
0
                        std::string_view secWebSocketExtensions = req->getHeader("sec-websocket-extensions");
412
0
                        memset((void *) secWebSocketExtensions.data(), ' ', secWebSocketExtensions.length());
413
0
                    }
414
415
0
                    behavior.upgrade(res, req, (struct us_socket_context_t *) webSocketContext);
416
33.5k
                } else {
417
                    /* Default handler upgrades to WebSocket */
418
33.5k
                    std::string_view secWebSocketProtocol = req->getHeader("sec-websocket-protocol");
419
33.5k
                    std::string_view secWebSocketExtensions = req->getHeader("sec-websocket-extensions");
420
421
                    /* Safari 15 hack */
422
33.5k
                    if (hasBrokenCompression(req->getHeader("user-agent"))) {
423
206
                        secWebSocketExtensions = "";
424
206
                    }
425
426
33.5k
                    res->template upgrade<UserData>({}, secWebSocketKey, secWebSocketProtocol, secWebSocketExtensions, (struct us_socket_context_t *) webSocketContext);
427
33.5k
                }
428
429
                /* We are going to get uncorked by the Http get return */
430
431
                /* We do not need to check for any close or shutdown here as we immediately return from get handler */
432
433
33.5k
            } else {
434
                /* Tell the router that we did not handle this request */
435
12.4k
                req->setYield(true);
436
12.4k
            }
437
46.0k
        }, true);
438
12.0k
        return std::move(*this);
439
12.0k
    }
440
441
    /* Browse to a server name, changing the router to this domain */
442
    TemplatedApp &&domain(std::string serverName) {
443
        HttpContextData<SSL> *httpContextData = httpContext->getSocketContextData();
444
445
        void *domainRouter = us_socket_context_find_server_name_userdata(SSL, (struct us_socket_context_t *) httpContext, serverName.c_str());
446
        if (domainRouter) {
447
            std::cout << "Browsed to SNI: " << serverName << std::endl;
448
            httpContextData->currentRouter = (decltype(httpContextData->currentRouter)) domainRouter;
449
        } else {
450
            std::cout << "Cannot browse to SNI: " << serverName << std::endl;
451
            httpContextData->currentRouter = &httpContextData->router;
452
        }
453
    
454
        return std::move(*this);
455
    }
456
457
6.03k
    TemplatedApp &&get(std::string pattern, MoveOnlyFunction<void(HttpResponse<SSL> *, HttpRequest *)> &&handler) {
458
6.03k
        if (httpContext) {
459
6.03k
            httpContext->onHttp("GET", pattern, std::move(handler));
460
6.03k
        }
461
6.03k
        return std::move(*this);
462
6.03k
    }
463
464
6.03k
    TemplatedApp &&post(std::string pattern, MoveOnlyFunction<void(HttpResponse<SSL> *, HttpRequest *)> &&handler) {
465
6.03k
        if (httpContext) {
466
6.03k
            httpContext->onHttp("POST", pattern, std::move(handler));
467
6.03k
        }
468
6.03k
        return std::move(*this);
469
6.03k
    }
470
471
    TemplatedApp &&options(std::string pattern, MoveOnlyFunction<void(HttpResponse<SSL> *, HttpRequest *)> &&handler) {
472
        if (httpContext) {
473
            httpContext->onHttp("OPTIONS", pattern, std::move(handler));
474
        }
475
        return std::move(*this);
476
    }
477
478
    TemplatedApp &&del(std::string pattern, MoveOnlyFunction<void(HttpResponse<SSL> *, HttpRequest *)> &&handler) {
479
        if (httpContext) {
480
            httpContext->onHttp("DELETE", pattern, std::move(handler));
481
        }
482
        return std::move(*this);
483
    }
484
485
    TemplatedApp &&patch(std::string pattern, MoveOnlyFunction<void(HttpResponse<SSL> *, HttpRequest *)> &&handler) {
486
        if (httpContext) {
487
            httpContext->onHttp("PATCH", pattern, std::move(handler));
488
        }
489
        return std::move(*this);
490
    }
491
492
    TemplatedApp &&put(std::string pattern, MoveOnlyFunction<void(HttpResponse<SSL> *, HttpRequest *)> &&handler) {
493
        if (httpContext) {
494
            httpContext->onHttp("PUT", pattern, std::move(handler));
495
        }
496
        return std::move(*this);
497
    }
498
499
    TemplatedApp &&head(std::string pattern, MoveOnlyFunction<void(HttpResponse<SSL> *, HttpRequest *)> &&handler) {
500
        if (httpContext) {
501
            httpContext->onHttp("HEAD", pattern, std::move(handler));
502
        }
503
        return std::move(*this);
504
    }
505
506
    TemplatedApp &&connect(std::string pattern, MoveOnlyFunction<void(HttpResponse<SSL> *, HttpRequest *)> &&handler) {
507
        if (httpContext) {
508
            httpContext->onHttp("CONNECT", pattern, std::move(handler));
509
        }
510
        return std::move(*this);
511
    }
512
513
    TemplatedApp &&trace(std::string pattern, MoveOnlyFunction<void(HttpResponse<SSL> *, HttpRequest *)> &&handler) {
514
        if (httpContext) {
515
            httpContext->onHttp("TRACE", pattern, std::move(handler));
516
        }
517
        return std::move(*this);
518
    }
519
520
    /* This one catches any method */
521
6.03k
    TemplatedApp &&any(std::string pattern, MoveOnlyFunction<void(HttpResponse<SSL> *, HttpRequest *)> &&handler) {
522
6.03k
        if (httpContext) {
523
6.03k
            httpContext->onHttp("*", pattern, std::move(handler));
524
6.03k
        }
525
6.03k
        return std::move(*this);
526
6.03k
    }
527
528
    /* Host, port, callback */
529
    TemplatedApp &&listen(std::string host, int port, MoveOnlyFunction<void(us_listen_socket_t *)> &&handler) {
530
        if (!host.length()) {
531
            return listen(port, std::move(handler));
532
        }
533
        handler(httpContext ? httpContext->listen(host.c_str(), port, 0) : nullptr);
534
        return std::move(*this);
535
    }
536
537
    /* Host, port, options, callback */
538
    TemplatedApp &&listen(std::string host, int port, int options, MoveOnlyFunction<void(us_listen_socket_t *)> &&handler) {
539
        if (!host.length()) {
540
            return listen(port, options, std::move(handler));
541
        }
542
        handler(httpContext ? httpContext->listen(host.c_str(), port, options) : nullptr);
543
        return std::move(*this);
544
    }
545
546
    /* Port, callback */
547
6.03k
    TemplatedApp &&listen(int port, MoveOnlyFunction<void(us_listen_socket_t *)> &&handler) {
548
6.03k
        handler(httpContext ? httpContext->listen(nullptr, port, 0) : nullptr);
549
6.03k
        return std::move(*this);
550
6.03k
    }
551
552
    /* Port, options, callback */
553
    TemplatedApp &&listen(int port, int options, MoveOnlyFunction<void(us_listen_socket_t *)> &&handler) {
554
        handler(httpContext ? httpContext->listen(nullptr, port, options) : nullptr);
555
        return std::move(*this);
556
    }
557
558
    /* options, callback, path to unix domain socket */
559
    TemplatedApp &&listen(int options, MoveOnlyFunction<void(us_listen_socket_t *)> &&handler, std::string path) {
560
        handler(httpContext ? httpContext->listen(path.c_str(), options) : nullptr);
561
        return std::move(*this);
562
    }
563
564
    /* callback, path to unix domain socket */
565
    TemplatedApp &&listen(MoveOnlyFunction<void(us_listen_socket_t *)> &&handler, std::string path) {
566
        handler(httpContext ? httpContext->listen(path.c_str(), 0) : nullptr);
567
        return std::move(*this);
568
    }
569
570
6.03k
    TemplatedApp &&run() {
571
6.03k
        uWS::run();
572
6.03k
        return std::move(*this);
573
6.03k
    }
574
575
};
576
577
typedef TemplatedApp<false> App;
578
typedef TemplatedApp<true> SSLApp;
579
580
}
581
582
#endif // UWS_APP_H