opal-up 0.0.2 → 0.0.3

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (69) hide show
  1. checksums.yaml +4 -4
  2. data/LICENSE +209 -0
  3. data/README.md +81 -28
  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 +1374 -0
  43. data/ext/up_ext/libuwebsockets.h +260 -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 +278 -0
  48. data/lib/up/node/rack_env.rb +2 -2
  49. data/lib/up/ruby/cluster_cli.rb +10 -0
  50. data/lib/up/ruby/rack_cluster.rb +26 -0
  51. data/lib/up/ruby/rack_env.rb +97 -0
  52. data/lib/up/ruby/rack_server.rb +26 -0
  53. data/lib/up/ruby/server_cli.rb +10 -0
  54. data/lib/up/u_web_socket/rack_env.rb +1 -1
  55. data/lib/up/version.rb +1 -1
  56. metadata +71 -18
  57. data/.gitignore +0 -5
  58. data/Gemfile +0 -2
  59. data/example_rack_app/Gemfile +0 -3
  60. data/example_rack_app/config.ru +0 -6
  61. data/example_rack_app/rack_app.rb +0 -5
  62. data/example_roda_app/Gemfile +0 -6
  63. data/example_roda_app/config.ru +0 -6
  64. data/example_roda_app/roda_app.rb +0 -37
  65. data/example_sinatra_app/Gemfile +0 -6
  66. data/example_sinatra_app/config.ru +0 -6
  67. data/example_sinatra_app/sinatra_app.rb +0 -7
  68. data/opal-up.gemspec +0 -27
  69. data/up_logo.svg +0 -256
@@ -0,0 +1,260 @@
1
+ /*
2
+ * Copyright 2022 Ciro Spaciari
3
+ *
4
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
5
+ * of this software and associated documentation files (the "Software"), to deal
6
+ * in the Software without restriction, including without limitation the rights
7
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
8
+ * copies of the Software, and to permit persons to whom the Software is
9
+ * furnished to do so, subject to the following conditions:
10
+ *
11
+ * The above copyright notice and this permission notice shall be included in all
12
+ * copies or substantial portions of the Software.
13
+ *
14
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
15
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
16
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
17
+ * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
18
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
19
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
20
+ * SOFTWARE.
21
+ */
22
+
23
+ #ifndef LIBUWS_CAPI_HEADER
24
+ #define LIBUWS_CAPI_HEADER
25
+
26
+ #include <stddef.h>
27
+ #include <stdbool.h>
28
+ #include <stdint.h>
29
+ #include "libusockets.h"
30
+
31
+ #ifdef __cplusplus
32
+ extern "C"
33
+ {
34
+ #endif
35
+ #ifdef _WIN32
36
+ # define DLL_EXPORT __declspec( dllexport )
37
+ #else
38
+ # define DLL_EXPORT
39
+ #endif
40
+
41
+ DLL_EXPORT typedef enum
42
+ {
43
+ /* These are not actual compression options */
44
+ _COMPRESSOR_MASK = 0x00FF,
45
+ _DECOMPRESSOR_MASK = 0x0F00,
46
+ /* Disabled, shared, shared are "special" values */
47
+ DISABLED = 0,
48
+ SHARED_COMPRESSOR = 1,
49
+ SHARED_DECOMPRESSOR = 1 << 8,
50
+ /* Highest 4 bits describe decompressor */
51
+ DEDICATED_DECOMPRESSOR_32KB = 15 << 8,
52
+ DEDICATED_DECOMPRESSOR_16KB = 14 << 8,
53
+ DEDICATED_DECOMPRESSOR_8KB = 13 << 8,
54
+ DEDICATED_DECOMPRESSOR_4KB = 12 << 8,
55
+ DEDICATED_DECOMPRESSOR_2KB = 11 << 8,
56
+ DEDICATED_DECOMPRESSOR_1KB = 10 << 8,
57
+ DEDICATED_DECOMPRESSOR_512B = 9 << 8,
58
+ /* Same as 32kb */
59
+ DEDICATED_DECOMPRESSOR = 15 << 8,
60
+
61
+ /* Lowest 8 bit describe compressor */
62
+ DEDICATED_COMPRESSOR_3KB = 9 << 4 | 1,
63
+ DEDICATED_COMPRESSOR_4KB = 9 << 4 | 2,
64
+ DEDICATED_COMPRESSOR_8KB = 10 << 4 | 3,
65
+ DEDICATED_COMPRESSOR_16KB = 11 << 4 | 4,
66
+ DEDICATED_COMPRESSOR_32KB = 12 << 4 | 5,
67
+ DEDICATED_COMPRESSOR_64KB = 13 << 4 | 6,
68
+ DEDICATED_COMPRESSOR_128KB = 14 << 4 | 7,
69
+ DEDICATED_COMPRESSOR_256KB = 15 << 4 | 8,
70
+ /* Same as 256kb */
71
+ DEDICATED_COMPRESSOR = 15 << 4 | 8
72
+ } uws_compress_options_t;
73
+
74
+ DLL_EXPORT typedef enum
75
+ {
76
+ CONTINUATION = 0,
77
+ TEXT = 1,
78
+ BINARY = 2,
79
+ CLOSE = 8,
80
+ PING = 9,
81
+ PONG = 10
82
+ } uws_opcode_t;
83
+
84
+ DLL_EXPORT typedef enum
85
+ {
86
+ BACKPRESSURE,
87
+ SUCCESS,
88
+ DROPPED
89
+ } uws_sendstatus_t;
90
+
91
+ DLL_EXPORT typedef struct
92
+ {
93
+
94
+ int port;
95
+ const char *host;
96
+ int options;
97
+ } uws_app_listen_config_t;
98
+
99
+ DLL_EXPORT typedef struct {
100
+ bool ok;
101
+ bool has_responded;
102
+ } uws_try_end_result_t;
103
+
104
+ DLL_EXPORT struct uws_app_s;
105
+ DLL_EXPORT struct uws_req_s;
106
+ DLL_EXPORT struct uws_res_s;
107
+ DLL_EXPORT struct uws_websocket_s;
108
+ DLL_EXPORT struct uws_header_iterator_s;
109
+ DLL_EXPORT typedef struct uws_app_s uws_app_t;
110
+ DLL_EXPORT typedef struct uws_req_s uws_req_t;
111
+ DLL_EXPORT typedef struct uws_res_s uws_res_t;
112
+ DLL_EXPORT typedef struct uws_socket_context_s uws_socket_context_t;
113
+ DLL_EXPORT typedef struct uws_websocket_s uws_websocket_t;
114
+
115
+ DLL_EXPORT typedef void (*uws_websocket_handler)(uws_websocket_t *ws, void* user_data);
116
+ DLL_EXPORT typedef void (*uws_websocket_message_handler)(uws_websocket_t *ws, const char *message, size_t length, uws_opcode_t opcode, void* user_data);
117
+ DLL_EXPORT typedef void (*uws_websocket_ping_pong_handler)(uws_websocket_t *ws, const char *message, size_t length, void* user_data);
118
+ DLL_EXPORT typedef void (*uws_websocket_close_handler)(uws_websocket_t *ws, int code, const char *message, size_t length, void* user_data);
119
+ DLL_EXPORT typedef void (*uws_websocket_upgrade_handler)(uws_res_t *response, uws_req_t *request, uws_socket_context_t *context, void* user_data);
120
+ DLL_EXPORT typedef void (*uws_websocket_subscription_handler)(uws_websocket_t *ws, const char *topic_name, size_t topic_name_length, int new_number_of_subscriber, int old_number_of_subscriber, void* user_data);
121
+
122
+ DLL_EXPORT typedef struct
123
+ {
124
+ uws_compress_options_t compression;
125
+ /* Maximum message size we can receive */
126
+ unsigned int maxPayloadLength;
127
+ /* 2 minutes timeout is good */
128
+ unsigned short idleTimeout;
129
+ /* 64kb backpressure is probably good */
130
+ unsigned int maxBackpressure;
131
+ bool closeOnBackpressureLimit;
132
+ /* This one depends on kernel timeouts and is a bad default */
133
+ bool resetIdleTimeoutOnSend;
134
+ /* A good default, esp. for newcomers */
135
+ bool sendPingsAutomatically;
136
+ /* Maximum socket lifetime in seconds before forced closure (defaults to disabled) */
137
+ unsigned short maxLifetime;
138
+ uws_websocket_upgrade_handler upgrade;
139
+ uws_websocket_handler open;
140
+ uws_websocket_message_handler message;
141
+ uws_websocket_handler drain;
142
+ uws_websocket_ping_pong_handler ping;
143
+ uws_websocket_ping_pong_handler pong;
144
+ uws_websocket_close_handler close;
145
+ uws_websocket_subscription_handler subscription;
146
+ } uws_socket_behavior_t;
147
+
148
+ DLL_EXPORT typedef void (*uws_listen_handler)(struct us_listen_socket_t *listen_socket, uws_app_listen_config_t config, void *user_data);
149
+ DLL_EXPORT typedef void (*uws_listen_domain_handler)(struct us_listen_socket_t *listen_socket, const char* domain, size_t domain_length, int options, void *user_data);
150
+ DLL_EXPORT typedef void (*uws_method_handler)(uws_res_t *response, uws_req_t *request, void *user_data);
151
+ DLL_EXPORT typedef void (*uws_filter_handler)(uws_res_t *response, int, void *user_data);
152
+ DLL_EXPORT typedef void (*uws_missing_server_handler)(const char *hostname, size_t hostname_length, void *user_data);
153
+ DLL_EXPORT typedef void (*uws_get_headers_server_handler)(const char *header_name, size_t header_name_size, const char *header_value, size_t header_value_size, void *user_data);
154
+ //Basic HTTP
155
+ DLL_EXPORT uws_app_t *uws_create_app(int ssl, struct us_socket_context_options_t options);
156
+ DLL_EXPORT void uws_app_destroy(int ssl, uws_app_t *app);
157
+ DLL_EXPORT void uws_app_get(int ssl, uws_app_t *app, const char *pattern, uws_method_handler handler, void *user_data);
158
+ DLL_EXPORT void uws_app_post(int ssl, uws_app_t *app, const char *pattern, uws_method_handler handler, void *user_data);
159
+ DLL_EXPORT void uws_app_options(int ssl, uws_app_t *app, const char *pattern, uws_method_handler handler, void *user_data);
160
+ DLL_EXPORT void uws_app_delete(int ssl, uws_app_t *app, const char *pattern, uws_method_handler handler, void *user_data);
161
+ DLL_EXPORT void uws_app_patch(int ssl, uws_app_t *app, const char *pattern, uws_method_handler handler, void *user_data);
162
+ DLL_EXPORT void uws_app_put(int ssl, uws_app_t *app, const char *pattern, uws_method_handler handler, void *user_data);
163
+ DLL_EXPORT void uws_app_head(int ssl, uws_app_t *app, const char *pattern, uws_method_handler handler, void *user_data);
164
+ DLL_EXPORT void uws_app_connect(int ssl, uws_app_t *app, const char *pattern, uws_method_handler handler, void *user_data);
165
+ DLL_EXPORT void uws_app_trace(int ssl, uws_app_t *app, const char *pattern, uws_method_handler handler, void *user_data);
166
+ DLL_EXPORT void uws_app_any(int ssl, uws_app_t *app, const char *pattern, uws_method_handler handler, void *user_data);
167
+
168
+ DLL_EXPORT void uws_app_run(int ssl, uws_app_t *);
169
+
170
+ DLL_EXPORT void uws_app_listen(int ssl, uws_app_t *app, int port, uws_listen_handler handler, void *user_data);
171
+ DLL_EXPORT void uws_app_listen_with_config(int ssl, uws_app_t *app, uws_app_listen_config_t config, uws_listen_handler handler, void *user_data);
172
+ DLL_EXPORT void uws_app_listen_domain(int ssl, uws_app_t *app, const char *domain, size_t domain_length, uws_listen_domain_handler handler, void *user_data);
173
+ DLL_EXPORT void uws_app_listen_domain_with_options(int ssl, uws_app_t *app, const char *domain,size_t domain_length, int options, uws_listen_domain_handler handler, void *user_data);
174
+ DLL_EXPORT void uws_app_domain(int ssl, uws_app_t *app, const char* server_name, size_t server_name_length);
175
+ DLL_EXPORT void uws_app_close(int ssl, uws_app_t *app);
176
+
177
+ DLL_EXPORT bool uws_constructor_failed(int ssl, uws_app_t *app);
178
+ DLL_EXPORT unsigned int uws_num_subscribers(int ssl, uws_app_t *app, const char *topic, size_t topic_length);
179
+ DLL_EXPORT bool uws_publish(int ssl, uws_app_t *app, const char *topic, size_t topic_length, const char *message, size_t message_length, uws_opcode_t opcode, bool compress);
180
+ DLL_EXPORT void *uws_get_native_handle(int ssl, uws_app_t *app);
181
+ DLL_EXPORT void uws_remove_server_name(int ssl, uws_app_t *app, const char *hostname_pattern, size_t hostname_pattern_length);
182
+ DLL_EXPORT void uws_add_server_name(int ssl, uws_app_t *app, const char *hostname_pattern, size_t hostname_pattern_length);
183
+ DLL_EXPORT void uws_add_server_name_with_options(int ssl, uws_app_t *app, const char *hostname_pattern, size_t hostname_pattern_length, struct us_socket_context_options_t options);
184
+ DLL_EXPORT void uws_missing_server_name(int ssl, uws_app_t *app, uws_missing_server_handler handler, void *user_data);
185
+ DLL_EXPORT void uws_filter(int ssl, uws_app_t *app, uws_filter_handler handler, void *user_data);
186
+
187
+ //WebSocket
188
+ DLL_EXPORT void uws_ws(int ssl, uws_app_t *app, const char *pattern, uws_socket_behavior_t behavior, void* user_data);
189
+ DLL_EXPORT void *uws_ws_get_user_data(int ssl, uws_websocket_t *ws);
190
+ DLL_EXPORT void uws_ws_close(int ssl, uws_websocket_t *ws);
191
+ DLL_EXPORT uws_sendstatus_t uws_ws_send(int ssl, uws_websocket_t *ws, const char *message, size_t length, uws_opcode_t opcode);
192
+ DLL_EXPORT uws_sendstatus_t uws_ws_send_with_options(int ssl, uws_websocket_t *ws, const char *message, size_t length, uws_opcode_t opcode, bool compress, bool fin);
193
+ DLL_EXPORT uws_sendstatus_t uws_ws_send_fragment(int ssl, uws_websocket_t *ws, const char *message, size_t length, bool compress);
194
+ DLL_EXPORT uws_sendstatus_t uws_ws_send_first_fragment(int ssl, uws_websocket_t *ws, const char *message, size_t length, bool compress);
195
+ DLL_EXPORT uws_sendstatus_t uws_ws_send_first_fragment_with_opcode(int ssl, uws_websocket_t *ws, const char *message, size_t length, uws_opcode_t opcode, bool compress);
196
+ DLL_EXPORT uws_sendstatus_t uws_ws_send_last_fragment(int ssl, uws_websocket_t *ws, const char *message, size_t length, bool compress);
197
+ DLL_EXPORT void uws_ws_end(int ssl, uws_websocket_t *ws, int code, const char *message, size_t length);
198
+ DLL_EXPORT void uws_ws_cork(int ssl, uws_websocket_t *ws, void (*handler)(void *user_data), void *user_data);
199
+
200
+ DLL_EXPORT bool uws_ws_subscribe(int ssl, uws_websocket_t *ws, const char *topic, size_t length);
201
+ DLL_EXPORT bool uws_ws_unsubscribe(int ssl, uws_websocket_t *ws, const char *topic, size_t length);
202
+ DLL_EXPORT bool uws_ws_is_subscribed(int ssl, uws_websocket_t *ws, const char *topic, size_t length);
203
+ DLL_EXPORT void uws_ws_iterate_topics(int ssl, uws_websocket_t *ws, void (*callback)(const char *topic, size_t length, void *user_data), void *user_data);
204
+ DLL_EXPORT bool uws_ws_publish(int ssl, uws_websocket_t *ws, const char *topic, size_t topic_length, const char *message, size_t message_length);
205
+ DLL_EXPORT bool uws_ws_publish_with_options(int ssl, uws_websocket_t *ws, const char *topic, size_t topic_length, const char *message, size_t message_length, uws_opcode_t opcode, bool compress);
206
+ DLL_EXPORT unsigned int uws_ws_get_buffered_amount(int ssl, uws_websocket_t *ws);
207
+ DLL_EXPORT size_t uws_ws_get_remote_address(int ssl, uws_websocket_t *ws, const char **dest);
208
+ DLL_EXPORT size_t uws_ws_get_remote_address_as_text(int ssl, uws_websocket_t *ws, const char **dest);
209
+
210
+ //Response
211
+ DLL_EXPORT void uws_res_end(int ssl, uws_res_t *res, const char *data, size_t length, bool close_connection);
212
+ DLL_EXPORT uws_try_end_result_t uws_res_try_end(int ssl, uws_res_t *res, const char *data, size_t length, uintmax_t total_size, bool close_connection);
213
+ DLL_EXPORT void uws_res_cork(int ssl, uws_res_t *res, void(*callback)(uws_res_t *res, void* user_data) ,void* user_data);
214
+ DLL_EXPORT void uws_res_pause(int ssl, uws_res_t *res);
215
+ DLL_EXPORT void uws_res_resume(int ssl, uws_res_t *res);
216
+ DLL_EXPORT void uws_res_write_continue(int ssl, uws_res_t *res);
217
+ DLL_EXPORT void uws_res_write_status(int ssl, uws_res_t *res, const char *status, size_t length);
218
+ DLL_EXPORT void uws_res_write_header(int ssl, uws_res_t *res, const char *key, size_t key_length, const char *value, size_t value_length);
219
+
220
+ DLL_EXPORT void uws_res_write_header_int(int ssl, uws_res_t *res, const char *key, size_t key_length, uint64_t value);
221
+ DLL_EXPORT void uws_res_end_without_body(int ssl, uws_res_t *res, bool close_connection);
222
+ DLL_EXPORT bool uws_res_write(int ssl, uws_res_t *res, const char *data, size_t length);
223
+ DLL_EXPORT uintmax_t uws_res_get_write_offset(int ssl, uws_res_t *res);
224
+ DLL_EXPORT void uws_res_override_write_offset(int ssl, uws_res_t *res, uintmax_t offset);
225
+ DLL_EXPORT bool uws_res_has_responded(int ssl, uws_res_t *res);
226
+ DLL_EXPORT void uws_res_on_writable(int ssl, uws_res_t *res, bool (*handler)(uws_res_t *res, uintmax_t, void *optional_data), void *user_data);
227
+ DLL_EXPORT void uws_res_on_aborted(int ssl, uws_res_t *res, void (*handler)(uws_res_t *res, void *optional_data), void *optional_data);
228
+ DLL_EXPORT void uws_res_on_data(int ssl, uws_res_t *res, void (*handler)(uws_res_t *res, const char *chunk, size_t chunk_length, bool is_end, void *optional_data), void *optional_data);
229
+ DLL_EXPORT void uws_res_upgrade(int ssl, uws_res_t *res, void *data, const char *sec_web_socket_key, size_t sec_web_socket_key_length, const char *sec_web_socket_protocol, size_t sec_web_socket_protocol_length, const char *sec_web_socket_extensions, size_t sec_web_socket_extensions_length, uws_socket_context_t *ws);
230
+ DLL_EXPORT size_t uws_res_get_remote_address(int ssl, uws_res_t *res, const char **dest);
231
+ DLL_EXPORT size_t uws_res_get_remote_address_as_text(int ssl, uws_res_t *res, const char **dest);
232
+ #ifdef UWS_WITH_PROXY
233
+ DLL_EXPORT size_t uws_res_get_proxied_remote_address(int ssl, uws_res_t *res, const char **dest);
234
+ DLL_EXPORT size_t uws_res_get_proxied_remote_address_as_text(int ssl, uws_res_t *res, const char **dest);
235
+ #endif
236
+ DLL_EXPORT void *uws_res_get_native_handle(int ssl, uws_res_t *res);
237
+
238
+ //Request
239
+ DLL_EXPORT bool uws_req_is_ancient(uws_req_t *res);
240
+ DLL_EXPORT bool uws_req_get_yield(uws_req_t *res);
241
+ DLL_EXPORT void uws_req_set_yield(uws_req_t *res, bool yield);
242
+ DLL_EXPORT size_t uws_req_get_url(uws_req_t *res, const char **dest);
243
+ DLL_EXPORT size_t uws_req_get_full_url(uws_req_t *res, const char **dest);
244
+ DLL_EXPORT size_t uws_req_get_method(uws_req_t *res, const char **dest);
245
+ DLL_EXPORT size_t uws_req_get_case_sensitive_method(uws_req_t *res, const char **dest);
246
+
247
+ DLL_EXPORT size_t uws_req_get_header(uws_req_t *res, const char *lower_case_header, size_t lower_case_header_length, const char **dest);
248
+ DLL_EXPORT void uws_req_for_each_header(uws_req_t *res, uws_get_headers_server_handler handler, void *user_data);
249
+ DLL_EXPORT size_t uws_req_get_query(uws_req_t *res, const char *key, size_t key_length, const char **dest);
250
+ DLL_EXPORT size_t uws_req_get_parameter(uws_req_t *res, unsigned short index, const char **dest);
251
+
252
+ DLL_EXPORT struct us_loop_t *uws_get_loop();
253
+ DLL_EXPORT struct us_loop_t *uws_get_loop_with_native(void* existing_native_loop);
254
+ DLL_EXPORT void uws_loop_defer(struct us_loop_t *loop, void( cb(void *user_data) ), void *user_data);
255
+
256
+ #ifdef __cplusplus
257
+ }
258
+ #endif
259
+
260
+ #endif
data/ext/up_ext/loop.c ADDED
@@ -0,0 +1,386 @@
1
+ /*
2
+ * Authored by Alex Hultman, 2018-2021.
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 LIBUS_USE_IO_URING
19
+
20
+ #include "libusockets.h"
21
+ #include "internal.h"
22
+ #include <stdlib.h>
23
+
24
+ /* The loop has 2 fallthrough polls */
25
+ void us_internal_loop_data_init(struct us_loop_t *loop, void (*wakeup_cb)(struct us_loop_t *loop),
26
+ void (*pre_cb)(struct us_loop_t *loop), void (*post_cb)(struct us_loop_t *loop)) {
27
+ loop->data.sweep_timer = us_create_timer(loop, 1, 0);
28
+ loop->data.recv_buf = malloc(LIBUS_RECV_BUFFER_LENGTH + LIBUS_RECV_BUFFER_PADDING * 2);
29
+ loop->data.ssl_data = 0;
30
+ loop->data.head = 0;
31
+ loop->data.iterator = 0;
32
+ loop->data.closed_head = 0;
33
+ loop->data.low_prio_head = 0;
34
+ loop->data.low_prio_budget = 0;
35
+
36
+ loop->data.pre_cb = pre_cb;
37
+ loop->data.post_cb = post_cb;
38
+ loop->data.iteration_nr = 0;
39
+
40
+ loop->data.wakeup_async = us_internal_create_async(loop, 1, 0);
41
+ us_internal_async_set(loop->data.wakeup_async, (void (*)(struct us_internal_async *)) wakeup_cb);
42
+ }
43
+
44
+ void us_internal_loop_data_free(struct us_loop_t *loop) {
45
+ #ifndef LIBUS_NO_SSL
46
+ us_internal_free_loop_ssl_data(loop);
47
+ #endif
48
+
49
+ free(loop->data.recv_buf);
50
+
51
+ us_timer_close(loop->data.sweep_timer);
52
+ us_internal_async_close(loop->data.wakeup_async);
53
+ }
54
+
55
+ void us_wakeup_loop(struct us_loop_t *loop) {
56
+ us_internal_async_wakeup(loop->data.wakeup_async);
57
+ }
58
+
59
+ void us_internal_loop_link(struct us_loop_t *loop, struct us_socket_context_t *context) {
60
+ /* Insert this context as the head of loop */
61
+ context->next = loop->data.head;
62
+ context->prev = 0;
63
+ if (loop->data.head) {
64
+ loop->data.head->prev = context;
65
+ }
66
+ loop->data.head = context;
67
+ }
68
+
69
+ /* Unlink is called before free */
70
+ void us_internal_loop_unlink(struct us_loop_t *loop, struct us_socket_context_t *context) {
71
+ if (loop->data.head == context) {
72
+ loop->data.head = context->next;
73
+ if (loop->data.head) {
74
+ loop->data.head->prev = 0;
75
+ }
76
+ } else {
77
+ context->prev->next = context->next;
78
+ if (context->next) {
79
+ context->next->prev = context->prev;
80
+ }
81
+ }
82
+ }
83
+
84
+ /* This functions should never run recursively */
85
+ void us_internal_timer_sweep(struct us_loop_t *loop) {
86
+ struct us_internal_loop_data_t *loop_data = &loop->data;
87
+ /* For all socket contexts in this loop */
88
+ for (loop_data->iterator = loop_data->head; loop_data->iterator; loop_data->iterator = loop_data->iterator->next) {
89
+
90
+ struct us_socket_context_t *context = loop_data->iterator;
91
+
92
+ /* Update this context's timestamps (this could be moved to loop and done once) */
93
+ context->global_tick++;
94
+ unsigned char short_ticks = context->timestamp = context->global_tick % 240;
95
+ unsigned char long_ticks = context->long_timestamp = (context->global_tick / 15) % 240;
96
+
97
+ /* Begin at head */
98
+ struct us_socket_t *s = context->head_sockets;
99
+ while (s) {
100
+ /* Seek until end or timeout found (tightest loop) */
101
+ while (1) {
102
+ /* We only read from 1 random cache line here */
103
+ if (short_ticks == s->timeout || long_ticks == s->long_timeout) {
104
+ break;
105
+ }
106
+
107
+ /* Did we reach the end without a find? */
108
+ if ((s = s->next) == 0) {
109
+ goto next_context;
110
+ }
111
+ }
112
+
113
+ /* Here we have a timeout to emit (slow path) */
114
+ context->iterator = s;
115
+
116
+ if (short_ticks == s->timeout) {
117
+ s->timeout = 255;
118
+ context->on_socket_timeout(s);
119
+ }
120
+
121
+ if (context->iterator == s && long_ticks == s->long_timeout) {
122
+ s->long_timeout = 255;
123
+ context->on_socket_long_timeout(s);
124
+ }
125
+
126
+ /* Check for unlink / link (if the event handler did not modify the chain, we step 1) */
127
+ if (s == context->iterator) {
128
+ s = s->next;
129
+ } else {
130
+ /* The iterator was changed by event handler */
131
+ s = context->iterator;
132
+ }
133
+ }
134
+ /* We always store a 0 to context->iterator here since we are no longer iterating this context */
135
+ next_context:
136
+ context->iterator = 0;
137
+ }
138
+ }
139
+
140
+ /* We do not want to block the loop with tons and tons of CPU-intensive work for SSL handshakes.
141
+ * Spread it out during many loop iterations, prioritizing already open connections, they are far
142
+ * easier on CPU */
143
+ static const int MAX_LOW_PRIO_SOCKETS_PER_LOOP_ITERATION = 5;
144
+
145
+ void us_internal_handle_low_priority_sockets(struct us_loop_t *loop) {
146
+ struct us_internal_loop_data_t *loop_data = &loop->data;
147
+ struct us_socket_t *s;
148
+
149
+ loop_data->low_prio_budget = MAX_LOW_PRIO_SOCKETS_PER_LOOP_ITERATION;
150
+
151
+ for (s = loop_data->low_prio_head; s && loop_data->low_prio_budget > 0; s = loop_data->low_prio_head, loop_data->low_prio_budget--) {
152
+ /* Unlink this socket from the low-priority queue */
153
+ loop_data->low_prio_head = s->next;
154
+ if (s->next) s->next->prev = 0;
155
+ s->next = 0;
156
+
157
+ us_internal_socket_context_link_socket(s->context, s);
158
+ us_poll_change(&s->p, us_socket_context(0, s)->loop, us_poll_events(&s->p) | LIBUS_SOCKET_READABLE);
159
+
160
+ s->low_prio_state = 2;
161
+ }
162
+ }
163
+
164
+ /* Note: Properly takes the linked list and timeout sweep into account */
165
+ void us_internal_free_closed_sockets(struct us_loop_t *loop) {
166
+ /* Free all closed sockets (maybe it is better to reverse order?) */
167
+ if (loop->data.closed_head) {
168
+ for (struct us_socket_t *s = loop->data.closed_head; s; ) {
169
+ struct us_socket_t *next = s->next;
170
+ us_poll_free((struct us_poll_t *) s, loop);
171
+ s = next;
172
+ }
173
+ loop->data.closed_head = 0;
174
+ }
175
+ }
176
+
177
+ void sweep_timer_cb(struct us_internal_callback_t *cb) {
178
+ us_internal_timer_sweep(cb->loop);
179
+ }
180
+
181
+ long long us_loop_iteration_number(struct us_loop_t *loop) {
182
+ return loop->data.iteration_nr;
183
+ }
184
+
185
+ /* These may have somewhat different meaning depending on the underlying event library */
186
+ void us_internal_loop_pre(struct us_loop_t *loop) {
187
+ loop->data.iteration_nr++;
188
+ us_internal_handle_low_priority_sockets(loop);
189
+ loop->data.pre_cb(loop);
190
+ }
191
+
192
+ void us_internal_loop_post(struct us_loop_t *loop) {
193
+ us_internal_free_closed_sockets(loop);
194
+ loop->data.post_cb(loop);
195
+ }
196
+
197
+ struct us_socket_t *us_adopt_accepted_socket(int ssl, struct us_socket_context_t *context, LIBUS_SOCKET_DESCRIPTOR accepted_fd,
198
+ unsigned int socket_ext_size, char *addr_ip, int addr_ip_length) {
199
+ #ifndef LIBUS_NO_SSL
200
+ if (ssl) {
201
+ return (struct us_socket_t *)us_internal_ssl_adopt_accepted_socket((struct us_internal_ssl_socket_context_t *)context, accepted_fd,
202
+ socket_ext_size, addr_ip, addr_ip_length);
203
+ }
204
+ #endif
205
+ struct us_poll_t *accepted_p = us_create_poll(context->loop, 0, sizeof(struct us_socket_t) - sizeof(struct us_poll_t) + socket_ext_size);
206
+ us_poll_init(accepted_p, accepted_fd, POLL_TYPE_SOCKET);
207
+ us_poll_start(accepted_p, context->loop, LIBUS_SOCKET_READABLE);
208
+
209
+ struct us_socket_t *s = (struct us_socket_t *) accepted_p;
210
+
211
+ s->context = context;
212
+ s->timeout = 255;
213
+ s->long_timeout = 255;
214
+ s->low_prio_state = 0;
215
+
216
+ /* We always use nodelay */
217
+ bsd_socket_nodelay(accepted_fd, 1);
218
+
219
+ us_internal_socket_context_link_socket(context, s);
220
+
221
+ context->on_open(s, 0, addr_ip, addr_ip_length);
222
+ return s;
223
+ }
224
+
225
+ void us_internal_dispatch_ready_poll(struct us_poll_t *p, int error, int events) {
226
+ switch (us_internal_poll_type(p)) {
227
+ case POLL_TYPE_CALLBACK: {
228
+ struct us_internal_callback_t *cb = (struct us_internal_callback_t *) p;
229
+ /* Timers, asyncs should accept (read), while UDP sockets should obviously not */
230
+ if (!cb->leave_poll_ready) {
231
+ /* Let's just have this macro to silence the CodeQL alert regarding empty function when using libuv */
232
+ #ifndef LIBUS_USE_LIBUV
233
+ us_internal_accept_poll_event(p);
234
+ #endif
235
+ }
236
+ cb->cb(cb->cb_expects_the_loop ? (struct us_internal_callback_t *) cb->loop : (struct us_internal_callback_t *) &cb->p);
237
+ }
238
+ break;
239
+ case POLL_TYPE_SEMI_SOCKET: {
240
+ /* Both connect and listen sockets are semi-sockets
241
+ * but they poll for different events */
242
+ if (us_poll_events(p) == LIBUS_SOCKET_WRITABLE) {
243
+ struct us_socket_t *s = (struct us_socket_t *) p;
244
+
245
+ /* It is perfectly possible to come here with an error */
246
+ if (error) {
247
+ /* Emit error, close without emitting on_close */
248
+ s->context->on_connect_error(s, 0);
249
+ us_socket_close_connecting(0, s);
250
+ } else {
251
+ /* All sockets poll for readable */
252
+ us_poll_change(p, s->context->loop, LIBUS_SOCKET_READABLE);
253
+
254
+ /* We always use nodelay */
255
+ bsd_socket_nodelay(us_poll_fd(p), 1);
256
+
257
+ /* We are now a proper socket */
258
+ us_internal_poll_set_type(p, POLL_TYPE_SOCKET);
259
+
260
+ /* If we used a connection timeout we have to reset it here */
261
+ us_socket_timeout(0, s, 0);
262
+
263
+ s->context->on_open(s, 1, 0, 0);
264
+ }
265
+ } else {
266
+ struct us_listen_socket_t *listen_socket = (struct us_listen_socket_t *) p;
267
+ struct bsd_addr_t addr;
268
+
269
+ LIBUS_SOCKET_DESCRIPTOR client_fd = bsd_accept_socket(us_poll_fd(p), &addr);
270
+ if (client_fd == LIBUS_SOCKET_ERROR) {
271
+ /* Todo: start timer here */
272
+
273
+ } else {
274
+
275
+ /* Todo: stop timer if any */
276
+
277
+ do {
278
+ struct us_socket_context_t *context = us_socket_context(0, &listen_socket->s);
279
+ /* See if we want to export the FD or keep it here (this event can be unset) */
280
+ if (context->on_pre_open == 0 || context->on_pre_open(client_fd) == client_fd) {
281
+
282
+ /* Adopt the newly accepted socket */
283
+ us_adopt_accepted_socket(0, context,
284
+ client_fd, listen_socket->socket_ext_size, bsd_addr_get_ip(&addr), bsd_addr_get_ip_length(&addr));
285
+
286
+ /* Exit accept loop if listen socket was closed in on_open handler */
287
+ if (us_socket_is_closed(0, &listen_socket->s)) {
288
+ break;
289
+ }
290
+
291
+ }
292
+
293
+ } while ((client_fd = bsd_accept_socket(us_poll_fd(p), &addr)) != LIBUS_SOCKET_ERROR);
294
+ }
295
+ }
296
+ }
297
+ break;
298
+ case POLL_TYPE_SOCKET_SHUT_DOWN:
299
+ case POLL_TYPE_SOCKET: {
300
+ /* We should only use s, no p after this point */
301
+ struct us_socket_t *s = (struct us_socket_t *) p;
302
+
303
+ /* Such as epollerr epollhup */
304
+ if (error) {
305
+ /* Todo: decide what code we give here */
306
+ s = us_socket_close(0, s, 0, NULL);
307
+ return;
308
+ }
309
+
310
+ if (events & LIBUS_SOCKET_WRITABLE) {
311
+ /* Note: if we failed a write as a socket of one loop then adopted
312
+ * to another loop, this will be wrong. Absurd case though */
313
+ s->context->loop->data.last_write_failed = 0;
314
+
315
+ s = s->context->on_writable(s);
316
+
317
+ if (us_socket_is_closed(0, s)) {
318
+ return;
319
+ }
320
+
321
+ /* If we have no failed write or if we shut down, then stop polling for more writable */
322
+ if (!s->context->loop->data.last_write_failed || us_socket_is_shut_down(0, s)) {
323
+ us_poll_change(&s->p, us_socket_context(0, s)->loop, us_poll_events(&s->p) & LIBUS_SOCKET_READABLE);
324
+ }
325
+ }
326
+
327
+ if (events & LIBUS_SOCKET_READABLE) {
328
+ /* Contexts may prioritize down sockets that are currently readable, e.g. when SSL handshake has to be done.
329
+ * SSL handshakes are CPU intensive, so we limit the number of handshakes per loop iteration, and move the rest
330
+ * to the low-priority queue */
331
+ if (s->context->is_low_prio(s)) {
332
+ if (s->low_prio_state == 2) {
333
+ s->low_prio_state = 0; /* Socket has been delayed and now it's time to process incoming data for one iteration */
334
+ } else if (s->context->loop->data.low_prio_budget > 0) {
335
+ s->context->loop->data.low_prio_budget--; /* Still having budget for this iteration - do normal processing */
336
+ } else {
337
+ us_poll_change(&s->p, us_socket_context(0, s)->loop, us_poll_events(&s->p) & LIBUS_SOCKET_WRITABLE);
338
+ us_internal_socket_context_unlink_socket(s->context, s);
339
+
340
+ /* Link this socket to the low-priority queue - we use a LIFO queue, to prioritize newer clients that are
341
+ * maybe not already timeouted - sounds unfair, but works better in real-life with smaller client-timeouts
342
+ * under high load */
343
+ s->prev = 0;
344
+ s->next = s->context->loop->data.low_prio_head;
345
+ if (s->next) s->next->prev = s;
346
+ s->context->loop->data.low_prio_head = s;
347
+
348
+ s->low_prio_state = 1;
349
+
350
+ break;
351
+ }
352
+ }
353
+
354
+ int length = bsd_recv(us_poll_fd(&s->p), s->context->loop->data.recv_buf + LIBUS_RECV_BUFFER_PADDING, LIBUS_RECV_BUFFER_LENGTH, 0);
355
+ if (length > 0) {
356
+ s = s->context->on_data(s, s->context->loop->data.recv_buf + LIBUS_RECV_BUFFER_PADDING, length);
357
+ } else if (!length) {
358
+ if (us_socket_is_shut_down(0, s)) {
359
+ /* We got FIN back after sending it */
360
+ /* Todo: We should give "CLEAN SHUTDOWN" as reason here */
361
+ s = us_socket_close(0, s, 0, NULL);
362
+ } else {
363
+ /* We got FIN, so stop polling for readable */
364
+ us_poll_change(&s->p, us_socket_context(0, s)->loop, us_poll_events(&s->p) & LIBUS_SOCKET_WRITABLE);
365
+ s = s->context->on_end(s);
366
+ }
367
+ } else if (length == LIBUS_SOCKET_ERROR && !bsd_would_block()) {
368
+ /* Todo: decide also here what kind of reason we should give */
369
+ s = us_socket_close(0, s, 0, NULL);
370
+ }
371
+ }
372
+ }
373
+ break;
374
+ }
375
+ }
376
+
377
+ /* Integration only requires the timer to be set up */
378
+ void us_loop_integrate(struct us_loop_t *loop) {
379
+ us_timer_set(loop->data.sweep_timer, (void (*)(struct us_timer_t *)) sweep_timer_cb, LIBUS_TIMEOUT_GRANULARITY * 1000, LIBUS_TIMEOUT_GRANULARITY * 1000);
380
+ }
381
+
382
+ void *us_loop_ext(struct us_loop_t *loop) {
383
+ return loop + 1;
384
+ }
385
+
386
+ #endif