/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&) constUnexecuted 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*) constLine | 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*) constLine | 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*) constLine | 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*) constLine | 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*) constLine | 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*) constLine | 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*) constLine | 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*) constLine | 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()() constLine | 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()() constLine | 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()() constLine | 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 |