Coverage Report

Created: 2024-04-25 06:10

/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
309k
    inline bool hasBrokenCompression(std::string_view userAgent) {
30
309k
        size_t posStart = userAgent.find(" Version/15.");
31
309k
        if (posStart == std::string_view::npos) return false;
32
32.3k
        posStart += 12;
33
34
32.3k
        size_t posEnd = userAgent.find(' ', posStart);
35
32.3k
        if (posEnd == std::string_view::npos) return false;
36
37
30.6k
        unsigned int minorVersion = 0;
38
30.6k
        auto result = std::from_chars(userAgent.data() + posStart, userAgent.data() + posEnd, minorVersion);
39
30.6k
        if (result.ec != std::errc()) return false;
40
22.9k
        if (result.ptr != userAgent.data() + posEnd) return false; // do not accept trailing chars
41
10.9k
        if (minorVersion > 3) return false; // we target just Safari 15.0 - 15.3
42
43
3.25k
        if (userAgent.find(" Safari/", posEnd) == std::string_view::npos) return false;
44
45
691
        return true;
46
3.25k
    }
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
19.9k
        operator struct us_socket_context_options_t() const {
72
19.9k
            struct us_socket_context_options_t socket_context_options;
73
19.9k
            memcpy(&socket_context_options, this, sizeof(SocketContextOptions));
74
19.9k
            return socket_context_options;
75
19.9k
        }
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.41k
    TemplatedApp &&addServerName(std::string hostname_pattern, SocketContextOptions options = {}) {
96
97
        /* Do nothing if not even on SSL */
98
6.41k
        if constexpr (SSL) {
99
            /* First we create a new router for this domain */
100
6.41k
            auto *domainRouter = new HttpRouter<typename HttpContextData<SSL>::RouterData>();
101
102
6.41k
            us_socket_context_add_server_name(SSL, (struct us_socket_context_t *) httpContext, hostname_pattern.c_str(), options, domainRouter);
103
6.41k
        }
104
105
6.41k
        return std::move(*this);
106
6.41k
    }
107
108
6.41k
    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.41k
        auto *domainRouter = us_socket_context_find_server_name_userdata(SSL, (struct us_socket_context_t *) httpContext, hostname_pattern.c_str());
112
6.41k
        if (domainRouter) {
113
0
            delete (HttpRouter<typename HttpContextData<SSL>::RouterData> *) domainRouter;
114
0
        }
115
116
6.41k
        us_socket_context_remove_server_name(SSL, (struct us_socket_context_t *) httpContext, hostname_pattern.c_str());
117
6.41k
        return std::move(*this);
118
6.41k
    }
119
120
6.41k
    TemplatedApp &&missingServerName(MoveOnlyFunction<void(const char *hostname)> handler) {
121
122
6.41k
        if (!constructorFailed()) {
123
6.41k
            httpContext->getSocketContextData()->missingServerNameHandler = std::move(handler);
124
125
6.41k
            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.41k
        }
132
133
6.41k
        return std::move(*this);
134
6.41k
    }
135
136
    /* Returns the SSL_CTX of this app, or nullptr. */
137
6.41k
    void *getNativeHandle() {
138
6.41k
        return us_socket_context_get_native_handle(SSL, (struct us_socket_context_t *) httpContext);
139
6.41k
    }
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
1.07M
    bool publish(std::string_view topic, std::string_view message, OpCode opCode, bool compress = false) {
152
        /* Anything big bypasses corking efforts */
153
1.07M
        if (message.length() >= LoopData::CORK_BUFFER_SIZE) {
154
0
            return topicTree->publishBig(nullptr, topic, {message, opCode, compress}, [](Subscriber *s, TopicTreeBigMessage &message) {
155
0
                auto *ws = (WebSocket<SSL, true, int> *) s->user;
156
157
                /* Send will drain if needed */
158
0
                ws->send(message.message, (OpCode)message.opCode, message.compress);
159
0
            });
160
1.07M
        } else {
161
1.07M
            return topicTree->publish(nullptr, topic, {std::string(message), opCode, compress});
162
1.07M
        }
163
1.07M
    }
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
34.4k
    ~TemplatedApp() {
178
        /* Let's just put everything here */
179
34.4k
        if (httpContext) {
180
19.9k
            httpContext->free();
181
182
            /* Free all our webSocketContexts in a type less way */
183
29.9k
            for (auto &webSocketContextDeleter : webSocketContextDeleters) {
184
29.9k
                webSocketContextDeleter();
185
29.9k
            }
186
19.9k
        }
187
188
        /* Delete TopicTree */
189
34.4k
        if (topicTree) {
190
17.6k
            delete topicTree;
191
192
            /* And unregister loop callbacks */
193
            /* We must unregister any loop post handler here */
194
17.6k
            Loop::get()->removePostHandler(topicTree);
195
17.6k
            Loop::get()->removePreHandler(topicTree);
196
17.6k
        }
197
34.4k
    }
uWS::TemplatedApp<false>::~TemplatedApp()
Line
Count
Source
177
29.0k
    ~TemplatedApp() {
178
        /* Let's just put everything here */
179
29.0k
        if (httpContext) {
180
14.5k
            httpContext->free();
181
182
            /* Free all our webSocketContexts in a type less way */
183
24.5k
            for (auto &webSocketContextDeleter : webSocketContextDeleters) {
184
24.5k
                webSocketContextDeleter();
185
24.5k
            }
186
14.5k
        }
187
188
        /* Delete TopicTree */
189
29.0k
        if (topicTree) {
190
12.2k
            delete topicTree;
191
192
            /* And unregister loop callbacks */
193
            /* We must unregister any loop post handler here */
194
12.2k
            Loop::get()->removePostHandler(topicTree);
195
12.2k
            Loop::get()->removePreHandler(topicTree);
196
12.2k
        }
197
29.0k
    }
uWS::TemplatedApp<true>::~TemplatedApp()
Line
Count
Source
177
5.39k
    ~TemplatedApp() {
178
        /* Let's just put everything here */
179
5.39k
        if (httpContext) {
180
5.39k
            httpContext->free();
181
182
            /* Free all our webSocketContexts in a type less way */
183
5.39k
            for (auto &webSocketContextDeleter : webSocketContextDeleters) {
184
5.39k
                webSocketContextDeleter();
185
5.39k
            }
186
5.39k
        }
187
188
        /* Delete TopicTree */
189
5.39k
        if (topicTree) {
190
5.39k
            delete topicTree;
191
192
            /* And unregister loop callbacks */
193
            /* We must unregister any loop post handler here */
194
5.39k
            Loop::get()->removePostHandler(topicTree);
195
5.39k
            Loop::get()->removePreHandler(topicTree);
196
5.39k
        }
197
5.39k
    }
198
199
    /* Disallow copying, only move */
200
    TemplatedApp(const TemplatedApp &other) = delete;
201
202
14.5k
    TemplatedApp(TemplatedApp &&other) {
203
        /* Move HttpContext */
204
14.5k
        httpContext = other.httpContext;
205
14.5k
        other.httpContext = nullptr;
206
207
        /* Move webSocketContextDeleters */
208
14.5k
        webSocketContextDeleters = std::move(other.webSocketContextDeleters);
209
210
14.5k
        webSocketContexts = std::move(other.webSocketContexts);
211
212
        /* Move TopicTree */
213
14.5k
        topicTree = other.topicTree;
214
14.5k
        other.topicTree = nullptr;
215
14.5k
    }
216
217
19.9k
    TemplatedApp(SocketContextOptions options = {}) {
218
19.9k
        httpContext = HttpContext<SSL>::create(Loop::get(), options);
219
220
        /* Register default handler for 404 (can be overridden by user) */
221
45.9k
        this->any("/*", [](auto *res, auto */*req*/) {
222
45.9k
        res->writeStatus("404 File Not Found");
223
45.9k
          res->end("<html><body><h1>File Not Found</h1><hr><i>uWebSockets/20 Server</i></body></html>");
224
45.9k
        });
auto uWS::TemplatedApp<false>::TemplatedApp(uWS::SocketContextOptions)::{lambda(auto:1*, auto:2*)#1}::operator()<uWS::HttpResponse<false>, uWS::HttpRequest>(uWS::HttpResponse<false>*, uWS::HttpRequest*) const
Line
Count
Source
221
26.3k
        this->any("/*", [](auto *res, auto */*req*/) {
222
26.3k
        res->writeStatus("404 File Not Found");
223
26.3k
          res->end("<html><body><h1>File Not Found</h1><hr><i>uWebSockets/20 Server</i></body></html>");
224
26.3k
        });
auto uWS::TemplatedApp<true>::TemplatedApp(uWS::SocketContextOptions)::{lambda(auto:1*, auto:2*)#1}::operator()<uWS::HttpResponse<true>, uWS::HttpRequest>(uWS::HttpResponse<true>*, uWS::HttpRequest*) const
Line
Count
Source
221
19.5k
        this->any("/*", [](auto *res, auto */*req*/) {
222
19.5k
        res->writeStatus("404 File Not Found");
223
19.5k
          res->end("<html><body><h1>File Not Found</h1><hr><i>uWebSockets/20 Server</i></body></html>");
224
19.5k
        });
225
19.9k
    }
uWS::TemplatedApp<false>::TemplatedApp(uWS::SocketContextOptions)
Line
Count
Source
217
14.5k
    TemplatedApp(SocketContextOptions options = {}) {
218
14.5k
        httpContext = HttpContext<SSL>::create(Loop::get(), options);
219
220
        /* Register default handler for 404 (can be overridden by user) */
221
14.5k
        this->any("/*", [](auto *res, auto */*req*/) {
222
14.5k
        res->writeStatus("404 File Not Found");
223
14.5k
          res->end("<html><body><h1>File Not Found</h1><hr><i>uWebSockets/20 Server</i></body></html>");
224
14.5k
        });
225
14.5k
    }
uWS::TemplatedApp<true>::TemplatedApp(uWS::SocketContextOptions)
Line
Count
Source
217
5.39k
    TemplatedApp(SocketContextOptions options = {}) {
218
5.39k
        httpContext = HttpContext<SSL>::create(Loop::get(), options);
219
220
        /* Register default handler for 404 (can be overridden by user) */
221
5.39k
        this->any("/*", [](auto *res, auto */*req*/) {
222
5.39k
        res->writeStatus("404 File Not Found");
223
5.39k
          res->end("<html><body><h1>File Not Found</h1><hr><i>uWebSockets/20 Server</i></body></html>");
224
5.39k
        });
225
5.39k
    }
226
227
6.41k
    bool constructorFailed() {
228
6.41k
        return !httpContext;
229
6.41k
    }
230
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
    }
268
269
    template <typename UserData>
270
29.9k
    TemplatedApp &&ws(std::string pattern, WebSocketBehavior<UserData> &&behavior) {
271
        /* Don't compile if alignment rules cannot be satisfied */
272
29.9k
        static_assert(alignof(UserData) <= LIBUS_EXT_ALIGNMENT,
273
29.9k
        "µWebSockets cannot satisfy UserData alignment requirements. You need to recompile µSockets with LIBUS_EXT_ALIGNMENT adjusted accordingly.");
274
275
29.9k
        if (!httpContext) {
276
0
            return std::move(*this);
277
0
        }
278
279
        /* Terminate on misleading idleTimeout values */
280
29.9k
        if (behavior.idleTimeout && behavior.idleTimeout < 8) {
281
0
            std::cerr << "Error: idleTimeout must be either 0 or greater than 8!" << std::endl;
282
0
            std::terminate();
283
0
        }
284
285
        /* Maximum idleTimeout is 16 minutes */
286
29.9k
        if (behavior.idleTimeout > 240 * 4) {
287
0
            std::cerr << "Error: idleTimeout must not be greater than 960 seconds!" << std::endl;
288
0
            std::terminate();
289
0
        }
290
291
        /* Maximum maxLifetime is 4 hours */
292
29.9k
        if (behavior.maxLifetime > 240) {
293
0
            std::cerr << "Error: maxLifetime must not be greater than 240 minutes!" << std::endl;
294
0
            std::terminate();
295
0
        }
296
297
        /* If we don't have a TopicTree yet, create one now */
298
29.9k
        if (!topicTree) {
299
300
17.6k
            bool needsUncork = false;
301
84.5k
            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
84.5k
                auto *ws = (WebSocket<SSL, true, int> *) s->user;
307
308
                /* If this is the first message we try and cork */
309
84.5k
                if (flags & TopicTree<TopicTreeMessage, TopicTreeBigMessage>::IteratorFlags::FIRST) {
310
14.7k
                    if (ws->canCork() && !ws->isCorked()) {
311
9.39k
                        ((AsyncSocket<SSL> *)ws)->cork();
312
9.39k
                        needsUncork = true;
313
9.39k
                    }
314
14.7k
                }
315
316
                /* If we ever overstep maxBackpresure, exit immediately */
317
84.5k
                if (WebSocket<SSL, true, int>::SendStatus::DROPPED == ws->send(message.message, (OpCode)message.opCode, message.compress)) {
318
495
                    if (needsUncork) {
319
283
                        ((AsyncSocket<SSL> *)ws)->uncork();
320
283
                        needsUncork = false;
321
283
                    }
322
                    /* Stop draining */
323
495
                    return true;
324
495
                }
325
326
                /* If this is the last message we uncork if we are corked */
327
84.0k
                if (flags & TopicTree<TopicTreeMessage, TopicTreeBigMessage>::IteratorFlags::LAST) {
328
                    /* We should not uncork in all cases? */
329
14.2k
                    if (needsUncork) {
330
12.5k
                        ((AsyncSocket<SSL> *)ws)->uncork();
331
12.5k
                    }
332
14.2k
                }
333
334
                /* Success */
335
84.0k
                return false;
336
84.5k
            });
EpollEchoServer.cpp:uWS::TemplatedApp<false>::ws<test()::PerSocketData>(std::__1::basic_string<char, std::__1::char_traits<char>, std::__1::allocator<char> >, uWS::TemplatedApp<false>::WebSocketBehavior<test()::PerSocketData>&&)::{lambda(uWS::Subscriber*, uWS::TopicTreeMessage&, uWS::TopicTree<uWS::TopicTreeMessage, uWS::TopicTreeBigMessage>::IteratorFlags)#1}::operator()(uWS::Subscriber*, uWS::TopicTreeMessage&, uWS::TopicTree<uWS::TopicTreeMessage, uWS::TopicTreeBigMessage>::IteratorFlags)
Line
Count
Source
301
34.2k
            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
34.2k
                auto *ws = (WebSocket<SSL, true, int> *) s->user;
307
308
                /* If this is the first message we try and cork */
309
34.2k
                if (flags & TopicTree<TopicTreeMessage, TopicTreeBigMessage>::IteratorFlags::FIRST) {
310
4.99k
                    if (ws->canCork() && !ws->isCorked()) {
311
2.59k
                        ((AsyncSocket<SSL> *)ws)->cork();
312
2.59k
                        needsUncork = true;
313
2.59k
                    }
314
4.99k
                }
315
316
                /* If we ever overstep maxBackpresure, exit immediately */
317
34.2k
                if (WebSocket<SSL, true, int>::SendStatus::DROPPED == ws->send(message.message, (OpCode)message.opCode, message.compress)) {
318
0
                    if (needsUncork) {
319
0
                        ((AsyncSocket<SSL> *)ws)->uncork();
320
0
                        needsUncork = false;
321
0
                    }
322
                    /* Stop draining */
323
0
                    return true;
324
0
                }
325
326
                /* If this is the last message we uncork if we are corked */
327
34.2k
                if (flags & TopicTree<TopicTreeMessage, TopicTreeBigMessage>::IteratorFlags::LAST) {
328
                    /* We should not uncork in all cases? */
329
4.99k
                    if (needsUncork) {
330
3.75k
                        ((AsyncSocket<SSL> *)ws)->uncork();
331
3.75k
                    }
332
4.99k
                }
333
334
                /* Success */
335
34.2k
                return false;
336
34.2k
            });
EpollEchoServerPubSub.cpp:uWS::TemplatedApp<true>::ws<test()::PerSocketData>(std::__1::basic_string<char, std::__1::char_traits<char>, std::__1::allocator<char> >, uWS::TemplatedApp<true>::WebSocketBehavior<test()::PerSocketData>&&)::{lambda(uWS::Subscriber*, uWS::TopicTreeMessage&, uWS::TopicTree<uWS::TopicTreeMessage, uWS::TopicTreeBigMessage>::IteratorFlags)#1}::operator()(uWS::Subscriber*, uWS::TopicTreeMessage&, uWS::TopicTree<uWS::TopicTreeMessage, uWS::TopicTreeBigMessage>::IteratorFlags)
Line
Count
Source
301
50.2k
            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
50.2k
                auto *ws = (WebSocket<SSL, true, int> *) s->user;
307
308
                /* If this is the first message we try and cork */
309
50.2k
                if (flags & TopicTree<TopicTreeMessage, TopicTreeBigMessage>::IteratorFlags::FIRST) {
310
9.71k
                    if (ws->canCork() && !ws->isCorked()) {
311
6.80k
                        ((AsyncSocket<SSL> *)ws)->cork();
312
6.80k
                        needsUncork = true;
313
6.80k
                    }
314
9.71k
                }
315
316
                /* If we ever overstep maxBackpresure, exit immediately */
317
50.2k
                if (WebSocket<SSL, true, int>::SendStatus::DROPPED == ws->send(message.message, (OpCode)message.opCode, message.compress)) {
318
495
                    if (needsUncork) {
319
283
                        ((AsyncSocket<SSL> *)ws)->uncork();
320
283
                        needsUncork = false;
321
283
                    }
322
                    /* Stop draining */
323
495
                    return true;
324
495
                }
325
326
                /* If this is the last message we uncork if we are corked */
327
49.7k
                if (flags & TopicTree<TopicTreeMessage, TopicTreeBigMessage>::IteratorFlags::LAST) {
328
                    /* We should not uncork in all cases? */
329
9.22k
                    if (needsUncork) {
330
8.75k
                        ((AsyncSocket<SSL> *)ws)->uncork();
331
8.75k
                    }
332
9.22k
                }
333
334
                /* Success */
335
49.7k
                return false;
336
50.2k
            });
Unexecuted instantiation: EpollHelloWorld.cpp:uWS::TemplatedApp<false>::ws<test()::PerSocketData>(std::__1::basic_string<char, std::__1::char_traits<char>, std::__1::allocator<char> >, uWS::TemplatedApp<false>::WebSocketBehavior<test()::PerSocketData>&&)::{lambda(uWS::Subscriber*, uWS::TopicTreeMessage&, uWS::TopicTree<uWS::TopicTreeMessage, uWS::TopicTreeBigMessage>::IteratorFlags)#1}::operator()(uWS::Subscriber*, uWS::TopicTreeMessage&, uWS::TopicTree<uWS::TopicTreeMessage, uWS::TopicTreeBigMessage>::IteratorFlags)
337
338
            /* And hook it up with the loop */
339
            /* We empty for both pre and post just to make sure */
340
5.08M
            Loop::get()->addPostHandler(topicTree, [topicTree = topicTree](Loop */*loop*/) {
341
                /* Commit pub/sub batches every loop iteration */
342
5.08M
                topicTree->drain();
343
5.08M
            });
EpollEchoServer.cpp:uWS::TemplatedApp<false>::ws<test()::PerSocketData>(std::__1::basic_string<char, std::__1::char_traits<char>, std::__1::allocator<char> >, uWS::TemplatedApp<false>::WebSocketBehavior<test()::PerSocketData>&&)::{lambda(uWS::Loop*)#1}::operator()(uWS::Loop*) const
Line
Count
Source
340
2.18M
            Loop::get()->addPostHandler(topicTree, [topicTree = topicTree](Loop */*loop*/) {
341
                /* Commit pub/sub batches every loop iteration */
342
2.18M
                topicTree->drain();
343
2.18M
            });
EpollEchoServerPubSub.cpp:uWS::TemplatedApp<true>::ws<test()::PerSocketData>(std::__1::basic_string<char, std::__1::char_traits<char>, std::__1::allocator<char> >, uWS::TemplatedApp<true>::WebSocketBehavior<test()::PerSocketData>&&)::{lambda(uWS::Loop*)#1}::operator()(uWS::Loop*) const
Line
Count
Source
340
1.29M
            Loop::get()->addPostHandler(topicTree, [topicTree = topicTree](Loop */*loop*/) {
341
                /* Commit pub/sub batches every loop iteration */
342
1.29M
                topicTree->drain();
343
1.29M
            });
EpollHelloWorld.cpp:uWS::TemplatedApp<false>::ws<test()::PerSocketData>(std::__1::basic_string<char, std::__1::char_traits<char>, std::__1::allocator<char> >, uWS::TemplatedApp<false>::WebSocketBehavior<test()::PerSocketData>&&)::{lambda(uWS::Loop*)#1}::operator()(uWS::Loop*) const
Line
Count
Source
340
1.59M
            Loop::get()->addPostHandler(topicTree, [topicTree = topicTree](Loop */*loop*/) {
341
                /* Commit pub/sub batches every loop iteration */
342
1.59M
                topicTree->drain();
343
1.59M
            });
344
345
5.08M
            Loop::get()->addPreHandler(topicTree, [topicTree = topicTree](Loop */*loop*/) {
346
                /* Commit pub/sub batches every loop iteration */
347
5.08M
                topicTree->drain();
348
5.08M
            });
EpollEchoServer.cpp:uWS::TemplatedApp<false>::ws<test()::PerSocketData>(std::__1::basic_string<char, std::__1::char_traits<char>, std::__1::allocator<char> >, uWS::TemplatedApp<false>::WebSocketBehavior<test()::PerSocketData>&&)::{lambda(uWS::Loop*)#2}::operator()(uWS::Loop*) const
Line
Count
Source
345
2.18M
            Loop::get()->addPreHandler(topicTree, [topicTree = topicTree](Loop */*loop*/) {
346
                /* Commit pub/sub batches every loop iteration */
347
2.18M
                topicTree->drain();
348
2.18M
            });
EpollEchoServerPubSub.cpp:uWS::TemplatedApp<true>::ws<test()::PerSocketData>(std::__1::basic_string<char, std::__1::char_traits<char>, std::__1::allocator<char> >, uWS::TemplatedApp<true>::WebSocketBehavior<test()::PerSocketData>&&)::{lambda(uWS::Loop*)#2}::operator()(uWS::Loop*) const
Line
Count
Source
345
1.29M
            Loop::get()->addPreHandler(topicTree, [topicTree = topicTree](Loop */*loop*/) {
346
                /* Commit pub/sub batches every loop iteration */
347
1.29M
                topicTree->drain();
348
1.29M
            });
EpollHelloWorld.cpp:uWS::TemplatedApp<false>::ws<test()::PerSocketData>(std::__1::basic_string<char, std::__1::char_traits<char>, std::__1::allocator<char> >, uWS::TemplatedApp<false>::WebSocketBehavior<test()::PerSocketData>&&)::{lambda(uWS::Loop*)#2}::operator()(uWS::Loop*) const
Line
Count
Source
345
1.59M
            Loop::get()->addPreHandler(topicTree, [topicTree = topicTree](Loop */*loop*/) {
346
                /* Commit pub/sub batches every loop iteration */
347
1.59M
                topicTree->drain();
348
1.59M
            });
349
17.6k
        }
350
351
        /* Every route has its own websocket context with its own behavior and user data type */
352
29.9k
        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
29.9k
        webSocketContextDeleters.push_back([webSocketContext]() {
356
29.9k
            webSocketContext->free();
357
29.9k
        });
EpollEchoServer.cpp:uWS::TemplatedApp<false>::ws<test()::PerSocketData>(std::__1::basic_string<char, std::__1::char_traits<char>, std::__1::allocator<char> >, uWS::TemplatedApp<false>::WebSocketBehavior<test()::PerSocketData>&&)::{lambda()#1}::operator()() const
Line
Count
Source
355
11.6k
        webSocketContextDeleters.push_back([webSocketContext]() {
356
11.6k
            webSocketContext->free();
357
11.6k
        });
EpollEchoServerPubSub.cpp:uWS::TemplatedApp<true>::ws<test()::PerSocketData>(std::__1::basic_string<char, std::__1::char_traits<char>, std::__1::allocator<char> >, uWS::TemplatedApp<true>::WebSocketBehavior<test()::PerSocketData>&&)::{lambda()#1}::operator()() const
Line
Count
Source
355
5.39k
        webSocketContextDeleters.push_back([webSocketContext]() {
356
5.39k
            webSocketContext->free();
357
5.39k
        });
EpollHelloWorld.cpp:uWS::TemplatedApp<false>::ws<test()::PerSocketData>(std::__1::basic_string<char, std::__1::char_traits<char>, std::__1::allocator<char> >, uWS::TemplatedApp<false>::WebSocketBehavior<test()::PerSocketData>&&)::{lambda()#1}::operator()() const
Line
Count
Source
355
12.8k
        webSocketContextDeleters.push_back([webSocketContext]() {
356
12.8k
            webSocketContext->free();
357
12.8k
        });
358
359
        /* We also keep this list for easy closing */
360
29.9k
        webSocketContexts.push_back((void *)webSocketContext);
361
362
        /* Quick fix to disable any compression if set */
363
#ifdef UWS_NO_ZLIB
364
        behavior.compression = DISABLED;
365
#endif
366
367
        /* If we are the first one to use compression, initialize it */
368
29.9k
        if (behavior.compression) {
369
17.5k
            LoopData *loopData = (LoopData *) us_loop_ext(us_socket_context_loop(SSL, webSocketContext->getSocketContext()));
370
371
            /* Initialize loop's deflate inflate streams */
372
17.5k
            if (!loopData->zlibContext) {
373
11.9k
                loopData->zlibContext = new ZlibContext;
374
11.9k
                loopData->inflationStream = new InflationStream(CompressOptions::DEDICATED_DECOMPRESSOR);
375
11.9k
                loopData->deflationStream = new DeflationStream(CompressOptions::DEDICATED_COMPRESSOR);
376
11.9k
            }
377
17.5k
        }
378
379
        /* Copy all handlers */
380
29.9k
        webSocketContext->getExt()->openHandler = std::move(behavior.open);
381
29.9k
        webSocketContext->getExt()->messageHandler = std::move(behavior.message);
382
29.9k
        webSocketContext->getExt()->droppedHandler = std::move(behavior.dropped);
383
29.9k
        webSocketContext->getExt()->drainHandler = std::move(behavior.drain);
384
29.9k
        webSocketContext->getExt()->subscriptionHandler = std::move(behavior.subscription);
385
309k
        webSocketContext->getExt()->closeHandler = std::move([closeHandler = std::move(behavior.close)](WebSocket<SSL, true, UserData> *ws, int code, std::string_view message) mutable {
386
309k
            if (closeHandler) {
387
308k
                closeHandler(ws, code, message);
388
308k
            }
389
390
            /* Destruct user data after returning from close handler */
391
309k
            ((UserData *) ws->getUserData())->~UserData();
392
309k
        });
EpollEchoServer.cpp:uWS::TemplatedApp<false>::ws<test()::PerSocketData>(std::__1::basic_string<char, std::__1::char_traits<char>, std::__1::allocator<char> >, uWS::TemplatedApp<false>::WebSocketBehavior<test()::PerSocketData>&&)::{lambda(uWS::WebSocket<false, true, test()::PerSocketData>*, int, std::__1::basic_string_view<char, std::__1::char_traits<char> >)#1}::operator()(uWS::WebSocket<false, true, test()::PerSocketData>*, int, std::__1::basic_string_view<char, std::__1::char_traits<char> >)
Line
Count
Source
385
164k
        webSocketContext->getExt()->closeHandler = std::move([closeHandler = std::move(behavior.close)](WebSocket<SSL, true, UserData> *ws, int code, std::string_view message) mutable {
386
164k
            if (closeHandler) {
387
164k
                closeHandler(ws, code, message);
388
164k
            }
389
390
            /* Destruct user data after returning from close handler */
391
164k
            ((UserData *) ws->getUserData())->~UserData();
392
164k
        });
EpollEchoServerPubSub.cpp:uWS::TemplatedApp<true>::ws<test()::PerSocketData>(std::__1::basic_string<char, std::__1::char_traits<char>, std::__1::allocator<char> >, uWS::TemplatedApp<true>::WebSocketBehavior<test()::PerSocketData>&&)::{lambda(uWS::WebSocket<true, true, test()::PerSocketData>*, int, std::__1::basic_string_view<char, std::__1::char_traits<char> >)#1}::operator()(uWS::WebSocket<true, true, test()::PerSocketData>*, int, std::__1::basic_string_view<char, std::__1::char_traits<char> >)
Line
Count
Source
385
105k
        webSocketContext->getExt()->closeHandler = std::move([closeHandler = std::move(behavior.close)](WebSocket<SSL, true, UserData> *ws, int code, std::string_view message) mutable {
386
105k
            if (closeHandler) {
387
105k
                closeHandler(ws, code, message);
388
105k
            }
389
390
            /* Destruct user data after returning from close handler */
391
105k
            ((UserData *) ws->getUserData())->~UserData();
392
105k
        });
EpollHelloWorld.cpp:uWS::TemplatedApp<false>::ws<test()::PerSocketData>(std::__1::basic_string<char, std::__1::char_traits<char>, std::__1::allocator<char> >, uWS::TemplatedApp<false>::WebSocketBehavior<test()::PerSocketData>&&)::{lambda(uWS::WebSocket<false, true, test()::PerSocketData>*, int, std::__1::basic_string_view<char, std::__1::char_traits<char> >)#1}::operator()(uWS::WebSocket<false, true, test()::PerSocketData>*, int, std::__1::basic_string_view<char, std::__1::char_traits<char> >)
Line
Count
Source
385
40.1k
        webSocketContext->getExt()->closeHandler = std::move([closeHandler = std::move(behavior.close)](WebSocket<SSL, true, UserData> *ws, int code, std::string_view message) mutable {
386
40.1k
            if (closeHandler) {
387
39.0k
                closeHandler(ws, code, message);
388
39.0k
            }
389
390
            /* Destruct user data after returning from close handler */
391
40.1k
            ((UserData *) ws->getUserData())->~UserData();
392
40.1k
        });
393
29.9k
        webSocketContext->getExt()->pingHandler = std::move(behavior.ping);
394
29.9k
        webSocketContext->getExt()->pongHandler = std::move(behavior.pong);
395
396
        /* Copy settings */
397
29.9k
        webSocketContext->getExt()->maxPayloadLength = behavior.maxPayloadLength;
398
29.9k
        webSocketContext->getExt()->maxBackpressure = behavior.maxBackpressure;
399
29.9k
        webSocketContext->getExt()->closeOnBackpressureLimit = behavior.closeOnBackpressureLimit;
400
29.9k
        webSocketContext->getExt()->resetIdleTimeoutOnSend = behavior.resetIdleTimeoutOnSend;
401
29.9k
        webSocketContext->getExt()->sendPingsAutomatically = behavior.sendPingsAutomatically;
402
29.9k
        webSocketContext->getExt()->maxLifetime = behavior.maxLifetime;
403
29.9k
        webSocketContext->getExt()->compression = behavior.compression;
404
405
        /* Calculate idleTimeoutCompnents */
406
29.9k
        webSocketContext->getExt()->calculateIdleTimeoutCompnents(behavior.idleTimeout);
407
408
329k
        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
329k
            std::string_view secWebSocketKey = req->getHeader("sec-websocket-key");
412
329k
            if (secWebSocketKey.length() == 24) {
413
414
                /* Emit upgrade handler */
415
309k
                if (behavior.upgrade) {
416
417
                    /* Nasty, ugly Safari 15 hack */
418
0
                    if (hasBrokenCompression(req->getHeader("user-agent"))) {
419
0
                        std::string_view secWebSocketExtensions = req->getHeader("sec-websocket-extensions");
420
0
                        memset((void *) secWebSocketExtensions.data(), ' ', secWebSocketExtensions.length());
421
0
                    }
422
423
0
                    behavior.upgrade(res, req, (struct us_socket_context_t *) webSocketContext);
424
309k
                } else {
425
                    /* Default handler upgrades to WebSocket */
426
309k
                    std::string_view secWebSocketProtocol = req->getHeader("sec-websocket-protocol");
427
309k
                    std::string_view secWebSocketExtensions = req->getHeader("sec-websocket-extensions");
428
429
                    /* Safari 15 hack */
430
309k
                    if (hasBrokenCompression(req->getHeader("user-agent"))) {
431
691
                        secWebSocketExtensions = "";
432
691
                    }
433
434
309k
                    res->template upgrade<UserData>({}, secWebSocketKey, secWebSocketProtocol, secWebSocketExtensions, (struct us_socket_context_t *) webSocketContext);
435
309k
                }
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
441
309k
            } else {
442
                /* Tell the router that we did not handle this request */
443
19.8k
                req->setYield(true);
444
19.8k
            }
445
329k
        }, true);
EpollEchoServer.cpp:auto uWS::TemplatedApp<false>::ws<test()::PerSocketData>(std::__1::basic_string<char, std::__1::char_traits<char>, std::__1::allocator<char> >, uWS::TemplatedApp<false>::WebSocketBehavior<test()::PerSocketData>&&)::{lambda(auto:1*, auto:2*)#1}::operator()<uWS::HttpResponse<false>, uWS::HttpRequest>(uWS::HttpResponse<false>*, uWS::HttpRequest*)
Line
Count
Source
408
169k
        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
169k
            std::string_view secWebSocketKey = req->getHeader("sec-websocket-key");
412
169k
            if (secWebSocketKey.length() == 24) {
413
414
                /* Emit upgrade handler */
415
164k
                if (behavior.upgrade) {
416
417
                    /* Nasty, ugly Safari 15 hack */
418
0
                    if (hasBrokenCompression(req->getHeader("user-agent"))) {
419
0
                        std::string_view secWebSocketExtensions = req->getHeader("sec-websocket-extensions");
420
0
                        memset((void *) secWebSocketExtensions.data(), ' ', secWebSocketExtensions.length());
421
0
                    }
422
423
0
                    behavior.upgrade(res, req, (struct us_socket_context_t *) webSocketContext);
424
164k
                } else {
425
                    /* Default handler upgrades to WebSocket */
426
164k
                    std::string_view secWebSocketProtocol = req->getHeader("sec-websocket-protocol");
427
164k
                    std::string_view secWebSocketExtensions = req->getHeader("sec-websocket-extensions");
428
429
                    /* Safari 15 hack */
430
164k
                    if (hasBrokenCompression(req->getHeader("user-agent"))) {
431
247
                        secWebSocketExtensions = "";
432
247
                    }
433
434
164k
                    res->template upgrade<UserData>({}, secWebSocketKey, secWebSocketProtocol, secWebSocketExtensions, (struct us_socket_context_t *) webSocketContext);
435
164k
                }
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
441
164k
            } else {
442
                /* Tell the router that we did not handle this request */
443
4.79k
                req->setYield(true);
444
4.79k
            }
445
169k
        }, true);
EpollEchoServerPubSub.cpp:auto uWS::TemplatedApp<true>::ws<test()::PerSocketData>(std::__1::basic_string<char, std::__1::char_traits<char>, std::__1::allocator<char> >, uWS::TemplatedApp<true>::WebSocketBehavior<test()::PerSocketData>&&)::{lambda(auto:1*, auto:2*)#1}::operator()<uWS::HttpResponse<true>, uWS::HttpRequest>(uWS::HttpResponse<true>*, uWS::HttpRequest*)
Line
Count
Source
408
109k
        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
109k
            std::string_view secWebSocketKey = req->getHeader("sec-websocket-key");
412
109k
            if (secWebSocketKey.length() == 24) {
413
414
                /* Emit upgrade handler */
415
105k
                if (behavior.upgrade) {
416
417
                    /* Nasty, ugly Safari 15 hack */
418
0
                    if (hasBrokenCompression(req->getHeader("user-agent"))) {
419
0
                        std::string_view secWebSocketExtensions = req->getHeader("sec-websocket-extensions");
420
0
                        memset((void *) secWebSocketExtensions.data(), ' ', secWebSocketExtensions.length());
421
0
                    }
422
423
0
                    behavior.upgrade(res, req, (struct us_socket_context_t *) webSocketContext);
424
105k
                } else {
425
                    /* Default handler upgrades to WebSocket */
426
105k
                    std::string_view secWebSocketProtocol = req->getHeader("sec-websocket-protocol");
427
105k
                    std::string_view secWebSocketExtensions = req->getHeader("sec-websocket-extensions");
428
429
                    /* Safari 15 hack */
430
105k
                    if (hasBrokenCompression(req->getHeader("user-agent"))) {
431
218
                        secWebSocketExtensions = "";
432
218
                    }
433
434
105k
                    res->template upgrade<UserData>({}, secWebSocketKey, secWebSocketProtocol, secWebSocketExtensions, (struct us_socket_context_t *) webSocketContext);
435
105k
                }
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
441
105k
            } else {
442
                /* Tell the router that we did not handle this request */
443
4.40k
                req->setYield(true);
444
4.40k
            }
445
109k
        }, true);
EpollHelloWorld.cpp:auto uWS::TemplatedApp<false>::ws<test()::PerSocketData>(std::__1::basic_string<char, std::__1::char_traits<char>, std::__1::allocator<char> >, uWS::TemplatedApp<false>::WebSocketBehavior<test()::PerSocketData>&&)::{lambda(auto:1*, auto:2*)#1}::operator()<uWS::HttpResponse<false>, uWS::HttpRequest>(uWS::HttpResponse<false>*, uWS::HttpRequest*)
Line
Count
Source
408
50.8k
        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
50.8k
            std::string_view secWebSocketKey = req->getHeader("sec-websocket-key");
412
50.8k
            if (secWebSocketKey.length() == 24) {
413
414
                /* Emit upgrade handler */
415
40.1k
                if (behavior.upgrade) {
416
417
                    /* Nasty, ugly Safari 15 hack */
418
0
                    if (hasBrokenCompression(req->getHeader("user-agent"))) {
419
0
                        std::string_view secWebSocketExtensions = req->getHeader("sec-websocket-extensions");
420
0
                        memset((void *) secWebSocketExtensions.data(), ' ', secWebSocketExtensions.length());
421
0
                    }
422
423
0
                    behavior.upgrade(res, req, (struct us_socket_context_t *) webSocketContext);
424
40.1k
                } else {
425
                    /* Default handler upgrades to WebSocket */
426
40.1k
                    std::string_view secWebSocketProtocol = req->getHeader("sec-websocket-protocol");
427
40.1k
                    std::string_view secWebSocketExtensions = req->getHeader("sec-websocket-extensions");
428
429
                    /* Safari 15 hack */
430
40.1k
                    if (hasBrokenCompression(req->getHeader("user-agent"))) {
431
226
                        secWebSocketExtensions = "";
432
226
                    }
433
434
40.1k
                    res->template upgrade<UserData>({}, secWebSocketKey, secWebSocketProtocol, secWebSocketExtensions, (struct us_socket_context_t *) webSocketContext);
435
40.1k
                }
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
441
40.1k
            } else {
442
                /* Tell the router that we did not handle this request */
443
10.6k
                req->setYield(true);
444
10.6k
            }
445
50.8k
        }, true);
446
29.9k
        return std::move(*this);
447
29.9k
    }
EpollEchoServer.cpp:uWS::TemplatedApp<false>&& uWS::TemplatedApp<false>::ws<test()::PerSocketData>(std::__1::basic_string<char, std::__1::char_traits<char>, std::__1::allocator<char> >, uWS::TemplatedApp<false>::WebSocketBehavior<test()::PerSocketData>&&)
Line
Count
Source
270
11.6k
    TemplatedApp &&ws(std::string pattern, WebSocketBehavior<UserData> &&behavior) {
271
        /* Don't compile if alignment rules cannot be satisfied */
272
11.6k
        static_assert(alignof(UserData) <= LIBUS_EXT_ALIGNMENT,
273
11.6k
        "µWebSockets cannot satisfy UserData alignment requirements. You need to recompile µSockets with LIBUS_EXT_ALIGNMENT adjusted accordingly.");
274
275
11.6k
        if (!httpContext) {
276
0
            return std::move(*this);
277
0
        }
278
279
        /* Terminate on misleading idleTimeout values */
280
11.6k
        if (behavior.idleTimeout && behavior.idleTimeout < 8) {
281
0
            std::cerr << "Error: idleTimeout must be either 0 or greater than 8!" << std::endl;
282
0
            std::terminate();
283
0
        }
284
285
        /* Maximum idleTimeout is 16 minutes */
286
11.6k
        if (behavior.idleTimeout > 240 * 4) {
287
0
            std::cerr << "Error: idleTimeout must not be greater than 960 seconds!" << std::endl;
288
0
            std::terminate();
289
0
        }
290
291
        /* Maximum maxLifetime is 4 hours */
292
11.6k
        if (behavior.maxLifetime > 240) {
293
0
            std::cerr << "Error: maxLifetime must not be greater than 240 minutes!" << std::endl;
294
0
            std::terminate();
295
0
        }
296
297
        /* If we don't have a TopicTree yet, create one now */
298
11.6k
        if (!topicTree) {
299
300
5.84k
            bool needsUncork = false;
301
5.84k
            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
5.84k
                auto *ws = (WebSocket<SSL, true, int> *) s->user;
307
308
                /* If this is the first message we try and cork */
309
5.84k
                if (flags & TopicTree<TopicTreeMessage, TopicTreeBigMessage>::IteratorFlags::FIRST) {
310
5.84k
                    if (ws->canCork() && !ws->isCorked()) {
311
5.84k
                        ((AsyncSocket<SSL> *)ws)->cork();
312
5.84k
                        needsUncork = true;
313
5.84k
                    }
314
5.84k
                }
315
316
                /* If we ever overstep maxBackpresure, exit immediately */
317
5.84k
                if (WebSocket<SSL, true, int>::SendStatus::DROPPED == ws->send(message.message, (OpCode)message.opCode, message.compress)) {
318
5.84k
                    if (needsUncork) {
319
5.84k
                        ((AsyncSocket<SSL> *)ws)->uncork();
320
5.84k
                        needsUncork = false;
321
5.84k
                    }
322
                    /* Stop draining */
323
5.84k
                    return true;
324
5.84k
                }
325
326
                /* If this is the last message we uncork if we are corked */
327
5.84k
                if (flags & TopicTree<TopicTreeMessage, TopicTreeBigMessage>::IteratorFlags::LAST) {
328
                    /* We should not uncork in all cases? */
329
5.84k
                    if (needsUncork) {
330
5.84k
                        ((AsyncSocket<SSL> *)ws)->uncork();
331
5.84k
                    }
332
5.84k
                }
333
334
                /* Success */
335
5.84k
                return false;
336
5.84k
            });
337
338
            /* And hook it up with the loop */
339
            /* We empty for both pre and post just to make sure */
340
5.84k
            Loop::get()->addPostHandler(topicTree, [topicTree = topicTree](Loop */*loop*/) {
341
                /* Commit pub/sub batches every loop iteration */
342
5.84k
                topicTree->drain();
343
5.84k
            });
344
345
5.84k
            Loop::get()->addPreHandler(topicTree, [topicTree = topicTree](Loop */*loop*/) {
346
                /* Commit pub/sub batches every loop iteration */
347
5.84k
                topicTree->drain();
348
5.84k
            });
349
5.84k
        }
350
351
        /* Every route has its own websocket context with its own behavior and user data type */
352
11.6k
        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
11.6k
        webSocketContextDeleters.push_back([webSocketContext]() {
356
11.6k
            webSocketContext->free();
357
11.6k
        });
358
359
        /* We also keep this list for easy closing */
360
11.6k
        webSocketContexts.push_back((void *)webSocketContext);
361
362
        /* Quick fix to disable any compression if set */
363
#ifdef UWS_NO_ZLIB
364
        behavior.compression = DISABLED;
365
#endif
366
367
        /* If we are the first one to use compression, initialize it */
368
11.6k
        if (behavior.compression) {
369
11.1k
            LoopData *loopData = (LoopData *) us_loop_ext(us_socket_context_loop(SSL, webSocketContext->getSocketContext()));
370
371
            /* Initialize loop's deflate inflate streams */
372
11.1k
            if (!loopData->zlibContext) {
373
5.56k
                loopData->zlibContext = new ZlibContext;
374
5.56k
                loopData->inflationStream = new InflationStream(CompressOptions::DEDICATED_DECOMPRESSOR);
375
5.56k
                loopData->deflationStream = new DeflationStream(CompressOptions::DEDICATED_COMPRESSOR);
376
5.56k
            }
377
11.1k
        }
378
379
        /* Copy all handlers */
380
11.6k
        webSocketContext->getExt()->openHandler = std::move(behavior.open);
381
11.6k
        webSocketContext->getExt()->messageHandler = std::move(behavior.message);
382
11.6k
        webSocketContext->getExt()->droppedHandler = std::move(behavior.dropped);
383
11.6k
        webSocketContext->getExt()->drainHandler = std::move(behavior.drain);
384
11.6k
        webSocketContext->getExt()->subscriptionHandler = std::move(behavior.subscription);
385
11.6k
        webSocketContext->getExt()->closeHandler = std::move([closeHandler = std::move(behavior.close)](WebSocket<SSL, true, UserData> *ws, int code, std::string_view message) mutable {
386
11.6k
            if (closeHandler) {
387
11.6k
                closeHandler(ws, code, message);
388
11.6k
            }
389
390
            /* Destruct user data after returning from close handler */
391
11.6k
            ((UserData *) ws->getUserData())->~UserData();
392
11.6k
        });
393
11.6k
        webSocketContext->getExt()->pingHandler = std::move(behavior.ping);
394
11.6k
        webSocketContext->getExt()->pongHandler = std::move(behavior.pong);
395
396
        /* Copy settings */
397
11.6k
        webSocketContext->getExt()->maxPayloadLength = behavior.maxPayloadLength;
398
11.6k
        webSocketContext->getExt()->maxBackpressure = behavior.maxBackpressure;
399
11.6k
        webSocketContext->getExt()->closeOnBackpressureLimit = behavior.closeOnBackpressureLimit;
400
11.6k
        webSocketContext->getExt()->resetIdleTimeoutOnSend = behavior.resetIdleTimeoutOnSend;
401
11.6k
        webSocketContext->getExt()->sendPingsAutomatically = behavior.sendPingsAutomatically;
402
11.6k
        webSocketContext->getExt()->maxLifetime = behavior.maxLifetime;
403
11.6k
        webSocketContext->getExt()->compression = behavior.compression;
404
405
        /* Calculate idleTimeoutCompnents */
406
11.6k
        webSocketContext->getExt()->calculateIdleTimeoutCompnents(behavior.idleTimeout);
407
408
11.6k
        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
11.6k
            std::string_view secWebSocketKey = req->getHeader("sec-websocket-key");
412
11.6k
            if (secWebSocketKey.length() == 24) {
413
414
                /* Emit upgrade handler */
415
11.6k
                if (behavior.upgrade) {
416
417
                    /* Nasty, ugly Safari 15 hack */
418
11.6k
                    if (hasBrokenCompression(req->getHeader("user-agent"))) {
419
11.6k
                        std::string_view secWebSocketExtensions = req->getHeader("sec-websocket-extensions");
420
11.6k
                        memset((void *) secWebSocketExtensions.data(), ' ', secWebSocketExtensions.length());
421
11.6k
                    }
422
423
11.6k
                    behavior.upgrade(res, req, (struct us_socket_context_t *) webSocketContext);
424
11.6k
                } else {
425
                    /* Default handler upgrades to WebSocket */
426
11.6k
                    std::string_view secWebSocketProtocol = req->getHeader("sec-websocket-protocol");
427
11.6k
                    std::string_view secWebSocketExtensions = req->getHeader("sec-websocket-extensions");
428
429
                    /* Safari 15 hack */
430
11.6k
                    if (hasBrokenCompression(req->getHeader("user-agent"))) {
431
11.6k
                        secWebSocketExtensions = "";
432
11.6k
                    }
433
434
11.6k
                    res->template upgrade<UserData>({}, secWebSocketKey, secWebSocketProtocol, secWebSocketExtensions, (struct us_socket_context_t *) webSocketContext);
435
11.6k
                }
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
441
11.6k
            } else {
442
                /* Tell the router that we did not handle this request */
443
11.6k
                req->setYield(true);
444
11.6k
            }
445
11.6k
        }, true);
446
11.6k
        return std::move(*this);
447
11.6k
    }
EpollEchoServerPubSub.cpp:uWS::TemplatedApp<true>&& uWS::TemplatedApp<true>::ws<test()::PerSocketData>(std::__1::basic_string<char, std::__1::char_traits<char>, std::__1::allocator<char> >, uWS::TemplatedApp<true>::WebSocketBehavior<test()::PerSocketData>&&)
Line
Count
Source
270
5.39k
    TemplatedApp &&ws(std::string pattern, WebSocketBehavior<UserData> &&behavior) {
271
        /* Don't compile if alignment rules cannot be satisfied */
272
5.39k
        static_assert(alignof(UserData) <= LIBUS_EXT_ALIGNMENT,
273
5.39k
        "µWebSockets cannot satisfy UserData alignment requirements. You need to recompile µSockets with LIBUS_EXT_ALIGNMENT adjusted accordingly.");
274
275
5.39k
        if (!httpContext) {
276
0
            return std::move(*this);
277
0
        }
278
279
        /* Terminate on misleading idleTimeout values */
280
5.39k
        if (behavior.idleTimeout && behavior.idleTimeout < 8) {
281
0
            std::cerr << "Error: idleTimeout must be either 0 or greater than 8!" << std::endl;
282
0
            std::terminate();
283
0
        }
284
285
        /* Maximum idleTimeout is 16 minutes */
286
5.39k
        if (behavior.idleTimeout > 240 * 4) {
287
0
            std::cerr << "Error: idleTimeout must not be greater than 960 seconds!" << std::endl;
288
0
            std::terminate();
289
0
        }
290
291
        /* Maximum maxLifetime is 4 hours */
292
5.39k
        if (behavior.maxLifetime > 240) {
293
0
            std::cerr << "Error: maxLifetime must not be greater than 240 minutes!" << std::endl;
294
0
            std::terminate();
295
0
        }
296
297
        /* If we don't have a TopicTree yet, create one now */
298
5.39k
        if (!topicTree) {
299
300
5.39k
            bool needsUncork = false;
301
5.39k
            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
5.39k
                auto *ws = (WebSocket<SSL, true, int> *) s->user;
307
308
                /* If this is the first message we try and cork */
309
5.39k
                if (flags & TopicTree<TopicTreeMessage, TopicTreeBigMessage>::IteratorFlags::FIRST) {
310
5.39k
                    if (ws->canCork() && !ws->isCorked()) {
311
5.39k
                        ((AsyncSocket<SSL> *)ws)->cork();
312
5.39k
                        needsUncork = true;
313
5.39k
                    }
314
5.39k
                }
315
316
                /* If we ever overstep maxBackpresure, exit immediately */
317
5.39k
                if (WebSocket<SSL, true, int>::SendStatus::DROPPED == ws->send(message.message, (OpCode)message.opCode, message.compress)) {
318
5.39k
                    if (needsUncork) {
319
5.39k
                        ((AsyncSocket<SSL> *)ws)->uncork();
320
5.39k
                        needsUncork = false;
321
5.39k
                    }
322
                    /* Stop draining */
323
5.39k
                    return true;
324
5.39k
                }
325
326
                /* If this is the last message we uncork if we are corked */
327
5.39k
                if (flags & TopicTree<TopicTreeMessage, TopicTreeBigMessage>::IteratorFlags::LAST) {
328
                    /* We should not uncork in all cases? */
329
5.39k
                    if (needsUncork) {
330
5.39k
                        ((AsyncSocket<SSL> *)ws)->uncork();
331
5.39k
                    }
332
5.39k
                }
333
334
                /* Success */
335
5.39k
                return false;
336
5.39k
            });
337
338
            /* And hook it up with the loop */
339
            /* We empty for both pre and post just to make sure */
340
5.39k
            Loop::get()->addPostHandler(topicTree, [topicTree = topicTree](Loop */*loop*/) {
341
                /* Commit pub/sub batches every loop iteration */
342
5.39k
                topicTree->drain();
343
5.39k
            });
344
345
5.39k
            Loop::get()->addPreHandler(topicTree, [topicTree = topicTree](Loop */*loop*/) {
346
                /* Commit pub/sub batches every loop iteration */
347
5.39k
                topicTree->drain();
348
5.39k
            });
349
5.39k
        }
350
351
        /* Every route has its own websocket context with its own behavior and user data type */
352
5.39k
        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
5.39k
        webSocketContextDeleters.push_back([webSocketContext]() {
356
5.39k
            webSocketContext->free();
357
5.39k
        });
358
359
        /* We also keep this list for easy closing */
360
5.39k
        webSocketContexts.push_back((void *)webSocketContext);
361
362
        /* Quick fix to disable any compression if set */
363
#ifdef UWS_NO_ZLIB
364
        behavior.compression = DISABLED;
365
#endif
366
367
        /* If we are the first one to use compression, initialize it */
368
5.39k
        if (behavior.compression) {
369
0
            LoopData *loopData = (LoopData *) us_loop_ext(us_socket_context_loop(SSL, webSocketContext->getSocketContext()));
370
371
            /* Initialize loop's deflate inflate streams */
372
0
            if (!loopData->zlibContext) {
373
0
                loopData->zlibContext = new ZlibContext;
374
0
                loopData->inflationStream = new InflationStream(CompressOptions::DEDICATED_DECOMPRESSOR);
375
0
                loopData->deflationStream = new DeflationStream(CompressOptions::DEDICATED_COMPRESSOR);
376
0
            }
377
0
        }
378
379
        /* Copy all handlers */
380
5.39k
        webSocketContext->getExt()->openHandler = std::move(behavior.open);
381
5.39k
        webSocketContext->getExt()->messageHandler = std::move(behavior.message);
382
5.39k
        webSocketContext->getExt()->droppedHandler = std::move(behavior.dropped);
383
5.39k
        webSocketContext->getExt()->drainHandler = std::move(behavior.drain);
384
5.39k
        webSocketContext->getExt()->subscriptionHandler = std::move(behavior.subscription);
385
5.39k
        webSocketContext->getExt()->closeHandler = std::move([closeHandler = std::move(behavior.close)](WebSocket<SSL, true, UserData> *ws, int code, std::string_view message) mutable {
386
5.39k
            if (closeHandler) {
387
5.39k
                closeHandler(ws, code, message);
388
5.39k
            }
389
390
            /* Destruct user data after returning from close handler */
391
5.39k
            ((UserData *) ws->getUserData())->~UserData();
392
5.39k
        });
393
5.39k
        webSocketContext->getExt()->pingHandler = std::move(behavior.ping);
394
5.39k
        webSocketContext->getExt()->pongHandler = std::move(behavior.pong);
395
396
        /* Copy settings */
397
5.39k
        webSocketContext->getExt()->maxPayloadLength = behavior.maxPayloadLength;
398
5.39k
        webSocketContext->getExt()->maxBackpressure = behavior.maxBackpressure;
399
5.39k
        webSocketContext->getExt()->closeOnBackpressureLimit = behavior.closeOnBackpressureLimit;
400
5.39k
        webSocketContext->getExt()->resetIdleTimeoutOnSend = behavior.resetIdleTimeoutOnSend;
401
5.39k
        webSocketContext->getExt()->sendPingsAutomatically = behavior.sendPingsAutomatically;
402
5.39k
        webSocketContext->getExt()->maxLifetime = behavior.maxLifetime;
403
5.39k
        webSocketContext->getExt()->compression = behavior.compression;
404
405
        /* Calculate idleTimeoutCompnents */
406
5.39k
        webSocketContext->getExt()->calculateIdleTimeoutCompnents(behavior.idleTimeout);
407
408
5.39k
        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
5.39k
            std::string_view secWebSocketKey = req->getHeader("sec-websocket-key");
412
5.39k
            if (secWebSocketKey.length() == 24) {
413
414
                /* Emit upgrade handler */
415
5.39k
                if (behavior.upgrade) {
416
417
                    /* Nasty, ugly Safari 15 hack */
418
5.39k
                    if (hasBrokenCompression(req->getHeader("user-agent"))) {
419
5.39k
                        std::string_view secWebSocketExtensions = req->getHeader("sec-websocket-extensions");
420
5.39k
                        memset((void *) secWebSocketExtensions.data(), ' ', secWebSocketExtensions.length());
421
5.39k
                    }
422
423
5.39k
                    behavior.upgrade(res, req, (struct us_socket_context_t *) webSocketContext);
424
5.39k
                } else {
425
                    /* Default handler upgrades to WebSocket */
426
5.39k
                    std::string_view secWebSocketProtocol = req->getHeader("sec-websocket-protocol");
427
5.39k
                    std::string_view secWebSocketExtensions = req->getHeader("sec-websocket-extensions");
428
429
                    /* Safari 15 hack */
430
5.39k
                    if (hasBrokenCompression(req->getHeader("user-agent"))) {
431
5.39k
                        secWebSocketExtensions = "";
432
5.39k
                    }
433
434
5.39k
                    res->template upgrade<UserData>({}, secWebSocketKey, secWebSocketProtocol, secWebSocketExtensions, (struct us_socket_context_t *) webSocketContext);
435
5.39k
                }
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
441
5.39k
            } else {
442
                /* Tell the router that we did not handle this request */
443
5.39k
                req->setYield(true);
444
5.39k
            }
445
5.39k
        }, true);
446
5.39k
        return std::move(*this);
447
5.39k
    }
EpollHelloWorld.cpp:uWS::TemplatedApp<false>&& uWS::TemplatedApp<false>::ws<test()::PerSocketData>(std::__1::basic_string<char, std::__1::char_traits<char>, std::__1::allocator<char> >, uWS::TemplatedApp<false>::WebSocketBehavior<test()::PerSocketData>&&)
Line
Count
Source
270
12.8k
    TemplatedApp &&ws(std::string pattern, WebSocketBehavior<UserData> &&behavior) {
271
        /* Don't compile if alignment rules cannot be satisfied */
272
12.8k
        static_assert(alignof(UserData) <= LIBUS_EXT_ALIGNMENT,
273
12.8k
        "µWebSockets cannot satisfy UserData alignment requirements. You need to recompile µSockets with LIBUS_EXT_ALIGNMENT adjusted accordingly.");
274
275
12.8k
        if (!httpContext) {
276
0
            return std::move(*this);
277
0
        }
278
279
        /* Terminate on misleading idleTimeout values */
280
12.8k
        if (behavior.idleTimeout && behavior.idleTimeout < 8) {
281
0
            std::cerr << "Error: idleTimeout must be either 0 or greater than 8!" << std::endl;
282
0
            std::terminate();
283
0
        }
284
285
        /* Maximum idleTimeout is 16 minutes */
286
12.8k
        if (behavior.idleTimeout > 240 * 4) {
287
0
            std::cerr << "Error: idleTimeout must not be greater than 960 seconds!" << std::endl;
288
0
            std::terminate();
289
0
        }
290
291
        /* Maximum maxLifetime is 4 hours */
292
12.8k
        if (behavior.maxLifetime > 240) {
293
0
            std::cerr << "Error: maxLifetime must not be greater than 240 minutes!" << std::endl;
294
0
            std::terminate();
295
0
        }
296
297
        /* If we don't have a TopicTree yet, create one now */
298
12.8k
        if (!topicTree) {
299
300
6.41k
            bool needsUncork = false;
301
6.41k
            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
6.41k
                auto *ws = (WebSocket<SSL, true, int> *) s->user;
307
308
                /* If this is the first message we try and cork */
309
6.41k
                if (flags & TopicTree<TopicTreeMessage, TopicTreeBigMessage>::IteratorFlags::FIRST) {
310
6.41k
                    if (ws->canCork() && !ws->isCorked()) {
311
6.41k
                        ((AsyncSocket<SSL> *)ws)->cork();
312
6.41k
                        needsUncork = true;
313
6.41k
                    }
314
6.41k
                }
315
316
                /* If we ever overstep maxBackpresure, exit immediately */
317
6.41k
                if (WebSocket<SSL, true, int>::SendStatus::DROPPED == ws->send(message.message, (OpCode)message.opCode, message.compress)) {
318
6.41k
                    if (needsUncork) {
319
6.41k
                        ((AsyncSocket<SSL> *)ws)->uncork();
320
6.41k
                        needsUncork = false;
321
6.41k
                    }
322
                    /* Stop draining */
323
6.41k
                    return true;
324
6.41k
                }
325
326
                /* If this is the last message we uncork if we are corked */
327
6.41k
                if (flags & TopicTree<TopicTreeMessage, TopicTreeBigMessage>::IteratorFlags::LAST) {
328
                    /* We should not uncork in all cases? */
329
6.41k
                    if (needsUncork) {
330
6.41k
                        ((AsyncSocket<SSL> *)ws)->uncork();
331
6.41k
                    }
332
6.41k
                }
333
334
                /* Success */
335
6.41k
                return false;
336
6.41k
            });
337
338
            /* And hook it up with the loop */
339
            /* We empty for both pre and post just to make sure */
340
6.41k
            Loop::get()->addPostHandler(topicTree, [topicTree = topicTree](Loop */*loop*/) {
341
                /* Commit pub/sub batches every loop iteration */
342
6.41k
                topicTree->drain();
343
6.41k
            });
344
345
6.41k
            Loop::get()->addPreHandler(topicTree, [topicTree = topicTree](Loop */*loop*/) {
346
                /* Commit pub/sub batches every loop iteration */
347
6.41k
                topicTree->drain();
348
6.41k
            });
349
6.41k
        }
350
351
        /* Every route has its own websocket context with its own behavior and user data type */
352
12.8k
        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
12.8k
        webSocketContextDeleters.push_back([webSocketContext]() {
356
12.8k
            webSocketContext->free();
357
12.8k
        });
358
359
        /* We also keep this list for easy closing */
360
12.8k
        webSocketContexts.push_back((void *)webSocketContext);
361
362
        /* Quick fix to disable any compression if set */
363
#ifdef UWS_NO_ZLIB
364
        behavior.compression = DISABLED;
365
#endif
366
367
        /* If we are the first one to use compression, initialize it */
368
12.8k
        if (behavior.compression) {
369
6.41k
            LoopData *loopData = (LoopData *) us_loop_ext(us_socket_context_loop(SSL, webSocketContext->getSocketContext()));
370
371
            /* Initialize loop's deflate inflate streams */
372
6.41k
            if (!loopData->zlibContext) {
373
6.41k
                loopData->zlibContext = new ZlibContext;
374
6.41k
                loopData->inflationStream = new InflationStream(CompressOptions::DEDICATED_DECOMPRESSOR);
375
6.41k
                loopData->deflationStream = new DeflationStream(CompressOptions::DEDICATED_COMPRESSOR);
376
6.41k
            }
377
6.41k
        }
378
379
        /* Copy all handlers */
380
12.8k
        webSocketContext->getExt()->openHandler = std::move(behavior.open);
381
12.8k
        webSocketContext->getExt()->messageHandler = std::move(behavior.message);
382
12.8k
        webSocketContext->getExt()->droppedHandler = std::move(behavior.dropped);
383
12.8k
        webSocketContext->getExt()->drainHandler = std::move(behavior.drain);
384
12.8k
        webSocketContext->getExt()->subscriptionHandler = std::move(behavior.subscription);
385
12.8k
        webSocketContext->getExt()->closeHandler = std::move([closeHandler = std::move(behavior.close)](WebSocket<SSL, true, UserData> *ws, int code, std::string_view message) mutable {
386
12.8k
            if (closeHandler) {
387
12.8k
                closeHandler(ws, code, message);
388
12.8k
            }
389
390
            /* Destruct user data after returning from close handler */
391
12.8k
            ((UserData *) ws->getUserData())->~UserData();
392
12.8k
        });
393
12.8k
        webSocketContext->getExt()->pingHandler = std::move(behavior.ping);
394
12.8k
        webSocketContext->getExt()->pongHandler = std::move(behavior.pong);
395
396
        /* Copy settings */
397
12.8k
        webSocketContext->getExt()->maxPayloadLength = behavior.maxPayloadLength;
398
12.8k
        webSocketContext->getExt()->maxBackpressure = behavior.maxBackpressure;
399
12.8k
        webSocketContext->getExt()->closeOnBackpressureLimit = behavior.closeOnBackpressureLimit;
400
12.8k
        webSocketContext->getExt()->resetIdleTimeoutOnSend = behavior.resetIdleTimeoutOnSend;
401
12.8k
        webSocketContext->getExt()->sendPingsAutomatically = behavior.sendPingsAutomatically;
402
12.8k
        webSocketContext->getExt()->maxLifetime = behavior.maxLifetime;
403
12.8k
        webSocketContext->getExt()->compression = behavior.compression;
404
405
        /* Calculate idleTimeoutCompnents */
406
12.8k
        webSocketContext->getExt()->calculateIdleTimeoutCompnents(behavior.idleTimeout);
407
408
12.8k
        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
12.8k
            std::string_view secWebSocketKey = req->getHeader("sec-websocket-key");
412
12.8k
            if (secWebSocketKey.length() == 24) {
413
414
                /* Emit upgrade handler */
415
12.8k
                if (behavior.upgrade) {
416
417
                    /* Nasty, ugly Safari 15 hack */
418
12.8k
                    if (hasBrokenCompression(req->getHeader("user-agent"))) {
419
12.8k
                        std::string_view secWebSocketExtensions = req->getHeader("sec-websocket-extensions");
420
12.8k
                        memset((void *) secWebSocketExtensions.data(), ' ', secWebSocketExtensions.length());
421
12.8k
                    }
422
423
12.8k
                    behavior.upgrade(res, req, (struct us_socket_context_t *) webSocketContext);
424
12.8k
                } else {
425
                    /* Default handler upgrades to WebSocket */
426
12.8k
                    std::string_view secWebSocketProtocol = req->getHeader("sec-websocket-protocol");
427
12.8k
                    std::string_view secWebSocketExtensions = req->getHeader("sec-websocket-extensions");
428
429
                    /* Safari 15 hack */
430
12.8k
                    if (hasBrokenCompression(req->getHeader("user-agent"))) {
431
12.8k
                        secWebSocketExtensions = "";
432
12.8k
                    }
433
434
12.8k
                    res->template upgrade<UserData>({}, secWebSocketKey, secWebSocketProtocol, secWebSocketExtensions, (struct us_socket_context_t *) webSocketContext);
435
12.8k
                }
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
441
12.8k
            } else {
442
                /* Tell the router that we did not handle this request */
443
12.8k
                req->setYield(true);
444
12.8k
            }
445
12.8k
        }, true);
446
12.8k
        return std::move(*this);
447
12.8k
    }
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
8.66k
    TemplatedApp &&get(std::string pattern, MoveOnlyFunction<void(HttpResponse<SSL> *, HttpRequest *)> &&handler) {
466
8.66k
        if (httpContext) {
467
8.66k
            httpContext->onHttp("GET", pattern, std::move(handler));
468
8.66k
        }
469
8.66k
        return std::move(*this);
470
8.66k
    }
471
472
6.41k
    TemplatedApp &&post(std::string pattern, MoveOnlyFunction<void(HttpResponse<SSL> *, HttpRequest *)> &&handler) {
473
6.41k
        if (httpContext) {
474
6.41k
            httpContext->onHttp("POST", pattern, std::move(handler));
475
6.41k
        }
476
6.41k
        return std::move(*this);
477
6.41k
    }
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
26.3k
    TemplatedApp &&any(std::string pattern, MoveOnlyFunction<void(HttpResponse<SSL> *, HttpRequest *)> &&handler) {
530
26.3k
        if (httpContext) {
531
26.3k
            httpContext->onHttp("*", pattern, std::move(handler));
532
26.3k
        }
533
26.3k
        return std::move(*this);
534
26.3k
    }
uWS::TemplatedApp<false>::any(std::__1::basic_string<char, std::__1::char_traits<char>, std::__1::allocator<char> >, ofats::any_invocable<void (uWS::HttpResponse<false>*, uWS::HttpRequest*)>&&)
Line
Count
Source
529
20.9k
    TemplatedApp &&any(std::string pattern, MoveOnlyFunction<void(HttpResponse<SSL> *, HttpRequest *)> &&handler) {
530
20.9k
        if (httpContext) {
531
20.9k
            httpContext->onHttp("*", pattern, std::move(handler));
532
20.9k
        }
533
20.9k
        return std::move(*this);
534
20.9k
    }
uWS::TemplatedApp<true>::any(std::__1::basic_string<char, std::__1::char_traits<char>, std::__1::allocator<char> >, ofats::any_invocable<void (uWS::HttpResponse<true>*, uWS::HttpRequest*)>&&)
Line
Count
Source
529
5.39k
    TemplatedApp &&any(std::string pattern, MoveOnlyFunction<void(HttpResponse<SSL> *, HttpRequest *)> &&handler) {
530
5.39k
        if (httpContext) {
531
5.39k
            httpContext->onHttp("*", pattern, std::move(handler));
532
5.39k
        }
533
5.39k
        return std::move(*this);
534
5.39k
    }
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
19.9k
    TemplatedApp &&listen(int port, MoveOnlyFunction<void(us_listen_socket_t *)> &&handler) {
556
19.9k
        handler(httpContext ? httpContext->listen(nullptr, port, 0) : nullptr);
557
19.9k
        return std::move(*this);
558
19.9k
    }
uWS::TemplatedApp<false>::listen(int, ofats::any_invocable<void (us_listen_socket_t*)>&&)
Line
Count
Source
555
14.5k
    TemplatedApp &&listen(int port, MoveOnlyFunction<void(us_listen_socket_t *)> &&handler) {
556
14.5k
        handler(httpContext ? httpContext->listen(nullptr, port, 0) : nullptr);
557
14.5k
        return std::move(*this);
558
14.5k
    }
uWS::TemplatedApp<true>::listen(int, ofats::any_invocable<void (us_listen_socket_t*)>&&)
Line
Count
Source
555
5.39k
    TemplatedApp &&listen(int port, MoveOnlyFunction<void(us_listen_socket_t *)> &&handler) {
556
5.39k
        handler(httpContext ? httpContext->listen(nullptr, port, 0) : nullptr);
557
5.39k
        return std::move(*this);
558
5.39k
    }
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
19.9k
    TemplatedApp &&run() {
591
19.9k
        uWS::run();
592
19.9k
        return std::move(*this);
593
19.9k
    }
uWS::TemplatedApp<false>::run()
Line
Count
Source
590
14.5k
    TemplatedApp &&run() {
591
14.5k
        uWS::run();
592
14.5k
        return std::move(*this);
593
14.5k
    }
uWS::TemplatedApp<true>::run()
Line
Count
Source
590
5.39k
    TemplatedApp &&run() {
591
5.39k
        uWS::run();
592
5.39k
        return std::move(*this);
593
5.39k
    }
594
595
    Loop *getLoop() {
596
        return (Loop *) httpContext->getLoop();
597
    }
598
599
};
600
601
typedef TemplatedApp<false> App;
602
typedef TemplatedApp<true> SSLApp;
603
604
}
605
606
#endif // UWS_APP_H