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