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