Coverage Report

Created: 2026-11-12 07:10

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