opal-up 0.0.2 → 0.0.4

Sign up to get free protection for your applications and to get access to all the features.
Files changed (81) hide show
  1. checksums.yaml +4 -4
  2. data/LICENSE +209 -0
  3. data/README.md +97 -29
  4. data/bin/up_ruby +4 -0
  5. data/bin/up_ruby_cluster +4 -0
  6. data/ext/up_ext/App.h +606 -0
  7. data/ext/up_ext/AsyncSocket.h +355 -0
  8. data/ext/up_ext/AsyncSocketData.h +87 -0
  9. data/ext/up_ext/BloomFilter.h +83 -0
  10. data/ext/up_ext/ChunkedEncoding.h +236 -0
  11. data/ext/up_ext/ClientApp.h +36 -0
  12. data/ext/up_ext/HttpContext.h +502 -0
  13. data/ext/up_ext/HttpContextData.h +56 -0
  14. data/ext/up_ext/HttpErrors.h +53 -0
  15. data/ext/up_ext/HttpParser.h +680 -0
  16. data/ext/up_ext/HttpResponse.h +578 -0
  17. data/ext/up_ext/HttpResponseData.h +95 -0
  18. data/ext/up_ext/HttpRouter.h +380 -0
  19. data/ext/up_ext/Loop.h +204 -0
  20. data/ext/up_ext/LoopData.h +112 -0
  21. data/ext/up_ext/MoveOnlyFunction.h +377 -0
  22. data/ext/up_ext/PerMessageDeflate.h +315 -0
  23. data/ext/up_ext/ProxyParser.h +163 -0
  24. data/ext/up_ext/QueryParser.h +120 -0
  25. data/ext/up_ext/TopicTree.h +363 -0
  26. data/ext/up_ext/Utilities.h +66 -0
  27. data/ext/up_ext/WebSocket.h +381 -0
  28. data/ext/up_ext/WebSocketContext.h +434 -0
  29. data/ext/up_ext/WebSocketContextData.h +109 -0
  30. data/ext/up_ext/WebSocketData.h +86 -0
  31. data/ext/up_ext/WebSocketExtensions.h +256 -0
  32. data/ext/up_ext/WebSocketHandshake.h +145 -0
  33. data/ext/up_ext/WebSocketProtocol.h +506 -0
  34. data/ext/up_ext/bsd.c +767 -0
  35. data/ext/up_ext/bsd.h +109 -0
  36. data/ext/up_ext/context.c +524 -0
  37. data/ext/up_ext/epoll_kqueue.c +458 -0
  38. data/ext/up_ext/epoll_kqueue.h +67 -0
  39. data/ext/up_ext/extconf.rb +5 -0
  40. data/ext/up_ext/internal.h +224 -0
  41. data/ext/up_ext/libusockets.h +350 -0
  42. data/ext/up_ext/libuwebsockets.cpp +1344 -0
  43. data/ext/up_ext/libuwebsockets.h +396 -0
  44. data/ext/up_ext/loop.c +386 -0
  45. data/ext/up_ext/loop_data.h +38 -0
  46. data/ext/up_ext/socket.c +231 -0
  47. data/ext/up_ext/up_ext.c +930 -0
  48. data/lib/up/bun/rack_env.rb +1 -13
  49. data/lib/up/bun/server.rb +93 -19
  50. data/lib/up/cli.rb +3 -0
  51. data/lib/up/client.rb +68 -0
  52. data/lib/up/ruby/cluster.rb +39 -0
  53. data/lib/up/ruby/cluster_cli.rb +10 -0
  54. data/lib/up/{node → ruby}/rack_cluster.rb +5 -4
  55. data/lib/up/{node → ruby}/rack_server.rb +4 -4
  56. data/lib/up/ruby/server_cli.rb +10 -0
  57. data/lib/up/u_web_socket/cluster.rb +18 -3
  58. data/lib/up/u_web_socket/server.rb +108 -15
  59. data/lib/up/version.rb +1 -1
  60. metadata +72 -30
  61. data/.gitignore +0 -5
  62. data/Gemfile +0 -2
  63. data/bin/up_node +0 -12
  64. data/bin/up_node_cluster +0 -12
  65. data/example_rack_app/Gemfile +0 -3
  66. data/example_rack_app/config.ru +0 -6
  67. data/example_rack_app/rack_app.rb +0 -5
  68. data/example_roda_app/Gemfile +0 -6
  69. data/example_roda_app/config.ru +0 -6
  70. data/example_roda_app/roda_app.rb +0 -37
  71. data/example_sinatra_app/Gemfile +0 -6
  72. data/example_sinatra_app/config.ru +0 -6
  73. data/example_sinatra_app/sinatra_app.rb +0 -7
  74. data/lib/up/node/cluster.rb +0 -39
  75. data/lib/up/node/cluster_cli.rb +0 -15
  76. data/lib/up/node/rack_env.rb +0 -106
  77. data/lib/up/node/server.rb +0 -84
  78. data/lib/up/node/server_cli.rb +0 -15
  79. data/lib/up/u_web_socket/rack_env.rb +0 -101
  80. data/opal-up.gemspec +0 -27
  81. data/up_logo.svg +0 -256
@@ -0,0 +1,502 @@
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_HTTPCONTEXT_H
19
+ #define UWS_HTTPCONTEXT_H
20
+
21
+ /* This class defines the main behavior of HTTP and emits various events */
22
+
23
+ #include "Loop.h"
24
+ #include "HttpContextData.h"
25
+ #include "HttpResponseData.h"
26
+ #include "AsyncSocket.h"
27
+ #include "WebSocketData.h"
28
+
29
+ #include <string_view>
30
+ #include <iostream>
31
+ #include "MoveOnlyFunction.h"
32
+
33
+ namespace uWS {
34
+ template<bool> struct HttpResponse;
35
+
36
+ template <bool SSL>
37
+ struct HttpContext {
38
+ template<bool> friend struct TemplatedApp;
39
+ template<bool> friend struct HttpResponse;
40
+ private:
41
+ HttpContext() = delete;
42
+
43
+ /* Maximum delay allowed until an HTTP connection is terminated due to outstanding request or rejected data (slow loris protection) */
44
+ static const int HTTP_IDLE_TIMEOUT_S = 10;
45
+
46
+ /* Minimum allowed receive throughput per second (clients uploading less than 16kB/sec get dropped) */
47
+ static const int HTTP_RECEIVE_THROUGHPUT_BYTES = 16 * 1024;
48
+
49
+ us_loop_t *getLoop() {
50
+ return us_socket_context_loop(SSL, getSocketContext());
51
+ }
52
+
53
+ us_socket_context_t *getSocketContext() {
54
+ return (us_socket_context_t *) this;
55
+ }
56
+
57
+ static us_socket_context_t *getSocketContext(us_socket_t *s) {
58
+ return (us_socket_context_t *) us_socket_context(SSL, s);
59
+ }
60
+
61
+ HttpContextData<SSL> *getSocketContextData() {
62
+ return (HttpContextData<SSL> *) us_socket_context_ext(SSL, getSocketContext());
63
+ }
64
+
65
+ static HttpContextData<SSL> *getSocketContextDataS(us_socket_t *s) {
66
+ return (HttpContextData<SSL> *) us_socket_context_ext(SSL, getSocketContext(s));
67
+ }
68
+
69
+ /* Init the HttpContext by registering libusockets event handlers */
70
+ HttpContext<SSL> *init() {
71
+ /* Handle socket connections */
72
+ us_socket_context_on_open(SSL, getSocketContext(), [](us_socket_t *s, int /*is_client*/, char */*ip*/, int /*ip_length*/) {
73
+ /* Any connected socket should timeout until it has a request */
74
+ us_socket_timeout(SSL, s, HTTP_IDLE_TIMEOUT_S);
75
+
76
+ /* Init socket ext */
77
+ new (us_socket_ext(SSL, s)) HttpResponseData<SSL>;
78
+
79
+ /* Call filter */
80
+ HttpContextData<SSL> *httpContextData = getSocketContextDataS(s);
81
+ for (auto &f : httpContextData->filterHandlers) {
82
+ f((HttpResponse<SSL> *) s, 1);
83
+ }
84
+
85
+ return s;
86
+ });
87
+
88
+ /* Handle socket disconnections */
89
+ us_socket_context_on_close(SSL, getSocketContext(), [](us_socket_t *s, int /*code*/, void */*reason*/) {
90
+ /* Get socket ext */
91
+ HttpResponseData<SSL> *httpResponseData = (HttpResponseData<SSL> *) us_socket_ext(SSL, s);
92
+
93
+ /* Call filter */
94
+ HttpContextData<SSL> *httpContextData = getSocketContextDataS(s);
95
+ for (auto &f : httpContextData->filterHandlers) {
96
+ f((HttpResponse<SSL> *) s, -1);
97
+ }
98
+
99
+ /* Signal broken HTTP request only if we have a pending request */
100
+ if (httpResponseData->onAborted) {
101
+ httpResponseData->onAborted();
102
+ }
103
+
104
+ /* Destruct socket ext */
105
+ httpResponseData->~HttpResponseData<SSL>();
106
+
107
+ return s;
108
+ });
109
+
110
+ /* Handle HTTP data streams */
111
+ us_socket_context_on_data(SSL, getSocketContext(), [](us_socket_t *s, char *data, int length) {
112
+
113
+ // total overhead is about 210k down to 180k
114
+ // ~210k req/sec is the original perf with write in data
115
+ // ~200k req/sec is with cork and formatting
116
+ // ~190k req/sec is with http parsing
117
+ // ~180k - 190k req/sec is with varying routing
118
+
119
+ HttpContextData<SSL> *httpContextData = getSocketContextDataS(s);
120
+
121
+ /* Do not accept any data while in shutdown state */
122
+ if (us_socket_is_shut_down(SSL, (us_socket_t *) s)) {
123
+ return s;
124
+ }
125
+
126
+ HttpResponseData<SSL> *httpResponseData = (HttpResponseData<SSL> *) us_socket_ext(SSL, s);
127
+
128
+ /* Cork this socket */
129
+ ((AsyncSocket<SSL> *) s)->cork();
130
+
131
+ /* Mark that we are inside the parser now */
132
+ httpContextData->isParsingHttp = true;
133
+
134
+ // clients need to know the cursor after http parse, not servers!
135
+ // how far did we read then? we need to know to continue with websocket parsing data? or?
136
+
137
+ void *proxyParser = nullptr;
138
+ #ifdef UWS_WITH_PROXY
139
+ proxyParser = &httpResponseData->proxyParser;
140
+ #endif
141
+
142
+ /* The return value is entirely up to us to interpret. The HttpParser only care for whether the returned value is DIFFERENT or not from passed user */
143
+ auto [err, returnedSocket] = httpResponseData->consumePostPadded(data, (unsigned int) length, s, proxyParser, [httpContextData](void *s, HttpRequest *httpRequest) -> void * {
144
+ /* For every request we reset the timeout and hang until user makes action */
145
+ /* Warning: if we are in shutdown state, resetting the timer is a security issue! */
146
+ us_socket_timeout(SSL, (us_socket_t *) s, 0);
147
+
148
+ /* Reset httpResponse */
149
+ HttpResponseData<SSL> *httpResponseData = (HttpResponseData<SSL> *) us_socket_ext(SSL, (us_socket_t *) s);
150
+ httpResponseData->offset = 0;
151
+
152
+ /* Are we not ready for another request yet? Terminate the connection. */
153
+ if (httpResponseData->state & HttpResponseData<SSL>::HTTP_RESPONSE_PENDING) {
154
+ us_socket_close(SSL, (us_socket_t *) s, 0, nullptr);
155
+ return nullptr;
156
+ }
157
+
158
+ /* Mark pending request and emit it */
159
+ httpResponseData->state = HttpResponseData<SSL>::HTTP_RESPONSE_PENDING;
160
+
161
+ /* Mark this response as connectionClose if ancient or connection: close */
162
+ if (httpRequest->isAncient() || httpRequest->getHeader("connection").length() == 5) {
163
+ httpResponseData->state |= HttpResponseData<SSL>::HTTP_CONNECTION_CLOSE;
164
+ }
165
+
166
+ /* Select the router based on SNI (only possible for SSL) */
167
+ auto *selectedRouter = &httpContextData->router;
168
+ if constexpr (SSL) {
169
+ void *domainRouter = us_socket_server_name_userdata(SSL, (struct us_socket_t *) s);
170
+ if (domainRouter) {
171
+ selectedRouter = (decltype(selectedRouter)) domainRouter;
172
+ }
173
+ }
174
+
175
+ /* Route the method and URL */
176
+ selectedRouter->getUserData() = {(HttpResponse<SSL> *) s, httpRequest};
177
+ if (!selectedRouter->route(httpRequest->getCaseSensitiveMethod(), httpRequest->getUrl())) {
178
+ /* We have to force close this socket as we have no handler for it */
179
+ us_socket_close(SSL, (us_socket_t *) s, 0, nullptr);
180
+ return nullptr;
181
+ }
182
+
183
+ /* First of all we need to check if this socket was deleted due to upgrade */
184
+ if (httpContextData->upgradedWebSocket) {
185
+ /* We differ between closed and upgraded below */
186
+ return nullptr;
187
+ }
188
+
189
+ /* Was the socket closed? */
190
+ if (us_socket_is_closed(SSL, (struct us_socket_t *) s)) {
191
+ return nullptr;
192
+ }
193
+
194
+ /* We absolutely have to terminate parsing if shutdown */
195
+ if (us_socket_is_shut_down(SSL, (us_socket_t *) s)) {
196
+ return nullptr;
197
+ }
198
+
199
+ /* Returning from a request handler without responding or attaching an onAborted handler is ill-use */
200
+ if (!((HttpResponse<SSL> *) s)->hasResponded() && !httpResponseData->onAborted) {
201
+ /* Throw exception here? */
202
+ std::cerr << "Error: Returning from a request handler without responding or attaching an abort handler is forbidden!" << std::endl;
203
+ std::terminate();
204
+ }
205
+
206
+ /* If we have not responded and we have a data handler, we need to timeout to enfore client sending the data */
207
+ if (!((HttpResponse<SSL> *) s)->hasResponded() && httpResponseData->inStream) {
208
+ us_socket_timeout(SSL, (us_socket_t *) s, HTTP_IDLE_TIMEOUT_S);
209
+ }
210
+
211
+ /* Continue parsing */
212
+ return s;
213
+
214
+ }, [httpResponseData](void *user, std::string_view data, bool fin) -> void * {
215
+ /* We always get an empty chunk even if there is no data */
216
+ if (httpResponseData->inStream) {
217
+
218
+ /* Todo: can this handle timeout for non-post as well? */
219
+ if (fin) {
220
+ /* If we just got the last chunk (or empty chunk), disable timeout */
221
+ us_socket_timeout(SSL, (struct us_socket_t *) user, 0);
222
+ } else {
223
+ /* We still have some more data coming in later, so reset timeout */
224
+ /* Only reset timeout if we got enough bytes (16kb/sec) since last time we reset here */
225
+ httpResponseData->received_bytes_per_timeout += (unsigned int) data.length();
226
+ if (httpResponseData->received_bytes_per_timeout >= HTTP_RECEIVE_THROUGHPUT_BYTES * HTTP_IDLE_TIMEOUT_S) {
227
+ us_socket_timeout(SSL, (struct us_socket_t *) user, HTTP_IDLE_TIMEOUT_S);
228
+ httpResponseData->received_bytes_per_timeout = 0;
229
+ }
230
+ }
231
+
232
+ /* We might respond in the handler, so do not change timeout after this */
233
+ httpResponseData->inStream(data, fin);
234
+
235
+ /* Was the socket closed? */
236
+ if (us_socket_is_closed(SSL, (struct us_socket_t *) user)) {
237
+ return nullptr;
238
+ }
239
+
240
+ /* We absolutely have to terminate parsing if shutdown */
241
+ if (us_socket_is_shut_down(SSL, (us_socket_t *) user)) {
242
+ return nullptr;
243
+ }
244
+
245
+ /* If we were given the last data chunk, reset data handler to ensure following
246
+ * requests on the same socket won't trigger any previously registered behavior */
247
+ if (fin) {
248
+ httpResponseData->inStream = nullptr;
249
+ }
250
+ }
251
+ return user;
252
+ });
253
+
254
+ /* Mark that we are no longer parsing Http */
255
+ httpContextData->isParsingHttp = false;
256
+
257
+ /* If we got fullptr that means the parser wants us to close the socket from error (same as calling the errorHandler) */
258
+ if (returnedSocket == FULLPTR) {
259
+ /* For errors, we only deliver them "at most once". We don't care if they get halfways delivered or not. */
260
+ us_socket_write(SSL, s, httpErrorResponses[err].data(), (int) httpErrorResponses[err].length(), false);
261
+ us_socket_shutdown(SSL, s);
262
+ /* Close any socket on HTTP errors */
263
+ us_socket_close(SSL, s, 0, nullptr);
264
+ /* This just makes the following code act as if the socket was closed from error inside the parser. */
265
+ returnedSocket = nullptr;
266
+ }
267
+
268
+ /* We need to uncork in all cases, except for nullptr (closed socket, or upgraded socket) */
269
+ if (returnedSocket != nullptr) {
270
+ /* Timeout on uncork failure */
271
+ auto [written, failed] = ((AsyncSocket<SSL> *) returnedSocket)->uncork();
272
+ if (failed) {
273
+ /* All Http sockets timeout by this, and this behavior match the one in HttpResponse::cork */
274
+ /* Warning: both HTTP_IDLE_TIMEOUT_S and HTTP_TIMEOUT_S are 10 seconds and both are used the same */
275
+ ((AsyncSocket<SSL> *) s)->timeout(HTTP_IDLE_TIMEOUT_S);
276
+ }
277
+
278
+ /* We need to check if we should close this socket here now */
279
+ if (httpResponseData->state & HttpResponseData<SSL>::HTTP_CONNECTION_CLOSE) {
280
+ if ((httpResponseData->state & HttpResponseData<SSL>::HTTP_RESPONSE_PENDING) == 0) {
281
+ if (((AsyncSocket<SSL> *) s)->getBufferedAmount() == 0) {
282
+ ((AsyncSocket<SSL> *) s)->shutdown();
283
+ /* We need to force close after sending FIN since we want to hinder
284
+ * clients from keeping to send their huge data */
285
+ ((AsyncSocket<SSL> *) s)->close();
286
+ }
287
+ }
288
+ }
289
+
290
+ return (us_socket_t *) returnedSocket;
291
+ }
292
+
293
+ /* If we upgraded, check here (differ between nullptr close and nullptr upgrade) */
294
+ if (httpContextData->upgradedWebSocket) {
295
+ /* This path is only for upgraded websockets */
296
+ AsyncSocket<SSL> *asyncSocket = (AsyncSocket<SSL> *) httpContextData->upgradedWebSocket;
297
+
298
+ /* Uncork here as well (note: what if we failed to uncork and we then pub/sub before we even upgraded?) */
299
+ auto [written, failed] = asyncSocket->uncork();
300
+
301
+ /* If we succeeded in uncorking, check if we have sent WebSocket FIN */
302
+ if (!failed) {
303
+ WebSocketData *webSocketData = (WebSocketData *) asyncSocket->getAsyncSocketData();
304
+ if (webSocketData->isShuttingDown) {
305
+ /* In that case, also send TCP FIN (this is similar to what we have in ws drain handler) */
306
+ asyncSocket->shutdown();
307
+ }
308
+ }
309
+
310
+ /* Reset upgradedWebSocket before we return */
311
+ httpContextData->upgradedWebSocket = nullptr;
312
+
313
+ /* Return the new upgraded websocket */
314
+ return (us_socket_t *) asyncSocket;
315
+ }
316
+
317
+ /* It is okay to uncork a closed socket and we need to */
318
+ ((AsyncSocket<SSL> *) s)->uncork();
319
+
320
+ /* We cannot return nullptr to the underlying stack in any case */
321
+ return s;
322
+ });
323
+
324
+ /* Handle HTTP write out (note: SSL_read may trigger this spuriously, the app need to handle spurious calls) */
325
+ us_socket_context_on_writable(SSL, getSocketContext(), [](us_socket_t *s) {
326
+
327
+ AsyncSocket<SSL> *asyncSocket = (AsyncSocket<SSL> *) s;
328
+ HttpResponseData<SSL> *httpResponseData = (HttpResponseData<SSL> *) asyncSocket->getAsyncSocketData();
329
+
330
+ /* Ask the developer to write data and return success (true) or failure (false), OR skip sending anything and return success (true). */
331
+ if (httpResponseData->onWritable) {
332
+ /* We are now writable, so hang timeout again, the user does not have to do anything so we should hang until end or tryEnd rearms timeout */
333
+ us_socket_timeout(SSL, s, 0);
334
+
335
+ /* We expect the developer to return whether or not write was successful (true).
336
+ * If write was never called, the developer should still return true so that we may drain. */
337
+ bool success = httpResponseData->callOnWritable(httpResponseData->offset);
338
+
339
+ /* The developer indicated that their onWritable failed. */
340
+ if (!success) {
341
+ /* Skip testing if we can drain anything since that might perform an extra syscall */
342
+ return s;
343
+ }
344
+
345
+ /* We don't want to fall through since we don't want to mess with timeout.
346
+ * It makes little sense to drain any backpressure when the user has registered onWritable. */
347
+ return s;
348
+ }
349
+
350
+ /* Drain any socket buffer, this might empty our backpressure and thus finish the request */
351
+ /*auto [written, failed] = */asyncSocket->write(nullptr, 0, true, 0);
352
+
353
+ /* Should we close this connection after a response - and is this response really done? */
354
+ if (httpResponseData->state & HttpResponseData<SSL>::HTTP_CONNECTION_CLOSE) {
355
+ if ((httpResponseData->state & HttpResponseData<SSL>::HTTP_RESPONSE_PENDING) == 0) {
356
+ if (asyncSocket->getBufferedAmount() == 0) {
357
+ asyncSocket->shutdown();
358
+ /* We need to force close after sending FIN since we want to hinder
359
+ * clients from keeping to send their huge data */
360
+ asyncSocket->close();
361
+ }
362
+ }
363
+ }
364
+
365
+ /* Expect another writable event, or another request within the timeout */
366
+ asyncSocket->timeout(HTTP_IDLE_TIMEOUT_S);
367
+
368
+ return s;
369
+ });
370
+
371
+ /* Handle FIN, HTTP does not support half-closed sockets, so simply close */
372
+ us_socket_context_on_end(SSL, getSocketContext(), [](us_socket_t *s) {
373
+
374
+ /* We do not care for half closed sockets */
375
+ AsyncSocket<SSL> *asyncSocket = (AsyncSocket<SSL> *) s;
376
+ return asyncSocket->close();
377
+
378
+ });
379
+
380
+ /* Handle socket timeouts, simply close them so to not confuse client with FIN */
381
+ us_socket_context_on_timeout(SSL, getSocketContext(), [](us_socket_t *s) {
382
+
383
+ /* Force close rather than gracefully shutdown and risk confusing the client with a complete download */
384
+ AsyncSocket<SSL> *asyncSocket = (AsyncSocket<SSL> *) s;
385
+ return asyncSocket->close();
386
+
387
+ });
388
+
389
+ return this;
390
+ }
391
+
392
+ public:
393
+ /* Construct a new HttpContext using specified loop */
394
+ static HttpContext *create(Loop *loop, us_socket_context_options_t options = {}) {
395
+ HttpContext *httpContext;
396
+
397
+ httpContext = (HttpContext *) us_create_socket_context(SSL, (us_loop_t *) loop, sizeof(HttpContextData<SSL>), options);
398
+
399
+ if (!httpContext) {
400
+ return nullptr;
401
+ }
402
+
403
+ /* Init socket context data */
404
+ new ((HttpContextData<SSL> *) us_socket_context_ext(SSL, (us_socket_context_t *) httpContext)) HttpContextData<SSL>();
405
+ return httpContext->init();
406
+ }
407
+
408
+ /* Destruct the HttpContext, it does not follow RAII */
409
+ void free() {
410
+ /* Destruct socket context data */
411
+ HttpContextData<SSL> *httpContextData = getSocketContextData();
412
+ httpContextData->~HttpContextData<SSL>();
413
+
414
+ /* Free the socket context in whole */
415
+ us_socket_context_free(SSL, getSocketContext());
416
+ }
417
+
418
+ void filter(MoveOnlyFunction<void(HttpResponse<SSL> *, int)> &&filterHandler) {
419
+ getSocketContextData()->filterHandlers.emplace_back(std::move(filterHandler));
420
+ }
421
+
422
+ /* Register an HTTP route handler acording to URL pattern */
423
+ void onHttp(std::string method, std::string pattern, MoveOnlyFunction<void(HttpResponse<SSL> *, HttpRequest *)> &&handler, bool upgrade = false) {
424
+ HttpContextData<SSL> *httpContextData = getSocketContextData();
425
+
426
+ /* Todo: This is ugly, fix */
427
+ std::vector<std::string> methods;
428
+ if (method == "*") {
429
+ methods = {"*"};
430
+ } else {
431
+ methods = {method};
432
+ }
433
+
434
+ uint32_t priority = method == "*" ? httpContextData->currentRouter->LOW_PRIORITY : (upgrade ? httpContextData->currentRouter->HIGH_PRIORITY : httpContextData->currentRouter->MEDIUM_PRIORITY);
435
+
436
+ /* If we are passed nullptr then remove this */
437
+ if (!handler) {
438
+ httpContextData->currentRouter->remove(methods[0], pattern, priority);
439
+ return;
440
+ }
441
+
442
+ /* Record this route's parameter offsets */
443
+ std::map<std::string, unsigned short, std::less<>> parameterOffsets;
444
+ unsigned short offset = 0;
445
+ for (unsigned int i = 0; i < pattern.length(); i++) {
446
+ if (pattern[i] == ':') {
447
+ i++;
448
+ unsigned int start = i;
449
+ while (i < pattern.length() && pattern[i] != '/') {
450
+ i++;
451
+ }
452
+ parameterOffsets[std::string(pattern.data() + start, i - start)] = offset;
453
+ //std::cout << "<" << std::string(pattern.data() + start, i - start) << "> is offset " << offset;
454
+ offset++;
455
+ }
456
+ }
457
+
458
+ httpContextData->currentRouter->add(methods, pattern, [handler = std::move(handler), parameterOffsets = std::move(parameterOffsets)](auto *r) mutable {
459
+ auto user = r->getUserData();
460
+ user.httpRequest->setYield(false);
461
+ user.httpRequest->setParameters(r->getParameters());
462
+ user.httpRequest->setParameterOffsets(&parameterOffsets);
463
+
464
+ /* Middleware? Automatically respond to expectations */
465
+ std::string_view expect = user.httpRequest->getHeader("expect");
466
+ if (expect.length() && expect == "100-continue") {
467
+ user.httpResponse->writeContinue();
468
+ }
469
+
470
+ handler(user.httpResponse, user.httpRequest);
471
+
472
+ /* If any handler yielded, the router will keep looking for a suitable handler. */
473
+ if (user.httpRequest->getYield()) {
474
+ return false;
475
+ }
476
+ return true;
477
+ }, priority);
478
+ }
479
+
480
+ /* Listen to port using this HttpContext */
481
+ us_listen_socket_t *listen(const char *host, int port, int options) {
482
+ return us_socket_context_listen(SSL, getSocketContext(), host, port, options, sizeof(HttpResponseData<SSL>));
483
+ }
484
+
485
+ /* Listen to unix domain socket using this HttpContext */
486
+ us_listen_socket_t *listen(const char *path, int options) {
487
+ return us_socket_context_listen_unix(SSL, getSocketContext(), path, options, sizeof(HttpResponseData<SSL>));
488
+ }
489
+
490
+ void onPreOpen(LIBUS_SOCKET_DESCRIPTOR (*handler)(LIBUS_SOCKET_DESCRIPTOR)) {
491
+ us_socket_context_on_pre_open(SSL, getSocketContext(), handler);
492
+ }
493
+
494
+ /* Adopt an externally accepted socket into this HttpContext */
495
+ us_socket_t *adoptAcceptedSocket(LIBUS_SOCKET_DESCRIPTOR accepted_fd) {
496
+ return us_adopt_accepted_socket(SSL, getSocketContext(), accepted_fd, sizeof(HttpResponseData<SSL>), 0, 0);
497
+ }
498
+ };
499
+
500
+ }
501
+
502
+ #endif // UWS_HTTPCONTEXT_H
@@ -0,0 +1,56 @@
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_HTTPCONTEXTDATA_H
19
+ #define UWS_HTTPCONTEXTDATA_H
20
+
21
+ #include "HttpRouter.h"
22
+
23
+ #include <vector>
24
+ #include "MoveOnlyFunction.h"
25
+
26
+ namespace uWS {
27
+ template<bool> struct HttpResponse;
28
+ struct HttpRequest;
29
+
30
+ template <bool SSL>
31
+ struct alignas(16) HttpContextData {
32
+ template <bool> friend struct HttpContext;
33
+ template <bool> friend struct HttpResponse;
34
+ template <bool> friend struct TemplatedApp;
35
+ private:
36
+ std::vector<MoveOnlyFunction<void(HttpResponse<SSL> *, int)>> filterHandlers;
37
+
38
+ MoveOnlyFunction<void(const char *hostname)> missingServerNameHandler;
39
+
40
+ struct RouterData {
41
+ HttpResponse<SSL> *httpResponse;
42
+ HttpRequest *httpRequest;
43
+ };
44
+
45
+ /* This is the currently browsed-to router when using SNI */
46
+ HttpRouter<RouterData> *currentRouter = &router;
47
+
48
+ /* This is the default router for default SNI or non-SSL */
49
+ HttpRouter<RouterData> router;
50
+ void *upgradedWebSocket = nullptr;
51
+ bool isParsingHttp = false;
52
+ };
53
+
54
+ }
55
+
56
+ #endif // UWS_HTTPCONTEXTDATA_H
@@ -0,0 +1,53 @@
1
+ /*
2
+ * Authored by Alex Hultman, 2018-2023.
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_HTTP_ERRORS
19
+ #define UWS_HTTP_ERRORS
20
+
21
+ #include <string_view>
22
+
23
+ namespace uWS {
24
+ /* Possible errors from http parsing */
25
+ enum HttpError {
26
+ HTTP_ERROR_505_HTTP_VERSION_NOT_SUPPORTED = 1,
27
+ HTTP_ERROR_431_REQUEST_HEADER_FIELDS_TOO_LARGE = 2,
28
+ HTTP_ERROR_400_BAD_REQUEST = 3
29
+ };
30
+
31
+ #ifndef UWS_HTTPRESPONSE_NO_WRITEMARK
32
+
33
+ /* Returned parser errors match this LUT. */
34
+ static const std::string_view httpErrorResponses[] = {
35
+ "", /* Zeroth place is no error so don't use it */
36
+ "HTTP/1.1 505 HTTP Version Not Supported\r\nConnection: close\r\n\r\n<h1>HTTP Version Not Supported</h1><p>This server does not support HTTP/1.0.</p><hr><i>uWebSockets/20 Server</i>",
37
+ "HTTP/1.1 431 Request Header Fields Too Large\r\nConnection: close\r\n\r\n<h1>Request Header Fields Too Large</h1><hr><i>uWebSockets/20 Server</i>",
38
+ "HTTP/1.1 400 Bad Request\r\nConnection: close\r\n\r\n<h1>Bad Request</h1><hr><i>uWebSockets/20 Server</i>",
39
+ };
40
+
41
+ #else
42
+ /* Anonymized pages */
43
+ static const std::string_view httpErrorResponses[] = {
44
+ "", /* Zeroth place is no error so don't use it */
45
+ "HTTP/1.1 505 HTTP Version Not Supported\r\nConnection: close\r\n\r\n",
46
+ "HTTP/1.1 431 Request Header Fields Too Large\r\nConnection: close\r\n\r\n",
47
+ "HTTP/1.1 400 Bad Request\r\nConnection: close\r\n\r\n"
48
+ };
49
+ #endif
50
+
51
+ }
52
+
53
+ #endif