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