iodine 0.7.60 → 0.8.0
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.
- checksums.yaml +4 -4
- data/.clang-format +15 -0
- data/.rspec +1 -0
- data/.yardopts +1 -3
- data/CHANGELOG.md +132 -12
- data/Gemfile +5 -7
- data/LICENSE.txt +1 -1
- data/README.md +147 -671
- data/Rakefile +9 -16
- data/examples/chatroom.nru +9 -0
- data/examples/client +74 -0
- data/examples/config-hello.nru +9 -0
- data/examples/config-hello.ru +11 -0
- data/examples/config-hijack.ru +13 -0
- data/examples/config-shootout.ru +43 -0
- data/examples/config.nru +120 -0
- data/examples/config.rb +37 -0
- data/examples/config.ru +42 -53
- data/examples/fake +24 -0
- data/examples/hello_neorack +137 -0
- data/examples/routes.nru +29 -0
- data/examples/routes.ru +42 -0
- data/exe/iodine +218 -258
- data/ext/iodine/extconf.rb +27 -100
- data/ext/iodine/fio-stl.h +128336 -0
- data/ext/iodine/fio-stl.md +26396 -0
- data/ext/iodine/iodine.c +213 -1393
- data/ext/iodine/iodine.h +250 -55
- data/ext/iodine/iodine_arg_helper.h +329 -0
- data/ext/iodine/iodine_caller.h +234 -25
- data/ext/iodine/iodine_cli.h +434 -0
- data/ext/iodine/iodine_compression.h +312 -0
- data/ext/iodine/iodine_connection.h +3484 -34
- data/ext/iodine/iodine_core.h +363 -0
- data/ext/iodine/iodine_crypto.h +1441 -0
- data/ext/iodine/iodine_defer.h +225 -4
- data/ext/iodine/iodine_json.h +738 -4
- data/ext/iodine/iodine_listener.h +263 -0
- data/ext/iodine/iodine_log.h +297 -0
- data/ext/iodine/iodine_minimap.h +543 -0
- data/ext/iodine/iodine_mustache.h +461 -4
- data/ext/iodine/iodine_pubsub_eng.h +729 -0
- data/ext/iodine/iodine_pubsub_history.h +547 -0
- data/ext/iodine/iodine_pubsub_msg.h +228 -0
- data/ext/iodine/iodine_redis.h +322 -0
- data/ext/iodine/iodine_store.h +418 -16
- data/ext/iodine/iodine_threads.h +279 -0
- data/ext/iodine/iodine_tls.h +515 -6
- data/ext/iodine/iodine_utils.h +923 -0
- data/iodine.gemspec +37 -42
- data/lib/iodine/benchmark.rb +1690 -0
- data/lib/iodine/documentation.rb +3085 -0
- data/lib/iodine/version.rb +17 -1
- data/lib/iodine.rb +54 -255
- data/logo.svg +36 -0
- data/mustache.md +55 -0
- data/sig/iodine.rbs +4 -0
- metadata +58 -184
- data/.github/ISSUE_TEMPLATE/bug_report.md +0 -40
- data/.github/workflows/ruby.yml +0 -42
- data/.gitignore +0 -21
- data/LIMITS.md +0 -41
- data/SPEC-PubSub-Draft.md +0 -159
- data/SPEC-WebSocket-Draft.md +0 -239
- data/bin/console +0 -22
- data/bin/info.md +0 -353
- data/bin/mustache_bench.rb +0 -100
- data/bin/poc/Gemfile.lock +0 -23
- data/bin/poc/README.md +0 -37
- data/bin/poc/config.ru +0 -66
- data/bin/poc/gemfile +0 -1
- data/bin/poc/www/index.html +0 -57
- data/examples/async_task.ru +0 -92
- data/examples/bates/README.md +0 -3
- data/examples/bates/config.ru +0 -342
- data/examples/bates/david+bold.pdf +0 -0
- data/examples/bates/public/drop-pdf.png +0 -0
- data/examples/bates/public/index.html +0 -600
- data/examples/echo.ru +0 -59
- data/examples/etag.ru +0 -16
- data/examples/hello.ru +0 -29
- data/examples/pubsub_engine.ru +0 -81
- data/examples/rack3.ru +0 -12
- data/examples/redis.ru +0 -70
- data/examples/shootout.ru +0 -73
- data/examples/sub-protocols.ru +0 -90
- data/examples/tcp_client.rb +0 -66
- data/examples/x-sendfile.ru +0 -14
- data/ext/iodine/fio.c +0 -11995
- data/ext/iodine/fio.h +0 -6371
- data/ext/iodine/fio_cli.c +0 -431
- data/ext/iodine/fio_cli.h +0 -189
- data/ext/iodine/fio_json_parser.h +0 -688
- data/ext/iodine/fio_siphash.c +0 -157
- data/ext/iodine/fio_siphash.h +0 -37
- data/ext/iodine/fio_tls.h +0 -129
- data/ext/iodine/fio_tls_missing.c +0 -649
- data/ext/iodine/fio_tls_openssl.c +0 -1106
- data/ext/iodine/fio_tmpfile.h +0 -50
- data/ext/iodine/fiobj.h +0 -44
- data/ext/iodine/fiobj4fio.h +0 -21
- data/ext/iodine/fiobj_ary.c +0 -333
- data/ext/iodine/fiobj_ary.h +0 -139
- data/ext/iodine/fiobj_data.c +0 -1187
- data/ext/iodine/fiobj_data.h +0 -167
- data/ext/iodine/fiobj_hash.c +0 -409
- data/ext/iodine/fiobj_hash.h +0 -176
- data/ext/iodine/fiobj_json.c +0 -622
- data/ext/iodine/fiobj_json.h +0 -68
- data/ext/iodine/fiobj_mem.h +0 -71
- data/ext/iodine/fiobj_mustache.c +0 -317
- data/ext/iodine/fiobj_mustache.h +0 -62
- data/ext/iodine/fiobj_numbers.c +0 -344
- data/ext/iodine/fiobj_numbers.h +0 -127
- data/ext/iodine/fiobj_str.c +0 -433
- data/ext/iodine/fiobj_str.h +0 -172
- data/ext/iodine/fiobject.c +0 -620
- data/ext/iodine/fiobject.h +0 -655
- data/ext/iodine/hpack.h +0 -1923
- data/ext/iodine/http.c +0 -2743
- data/ext/iodine/http.h +0 -1002
- data/ext/iodine/http1.c +0 -910
- data/ext/iodine/http1.h +0 -29
- data/ext/iodine/http1_parser.h +0 -878
- data/ext/iodine/http_internal.c +0 -1279
- data/ext/iodine/http_internal.h +0 -237
- data/ext/iodine/http_mime_parser.h +0 -350
- data/ext/iodine/iodine_caller.c +0 -230
- data/ext/iodine/iodine_connection.c +0 -941
- data/ext/iodine/iodine_defer.c +0 -420
- data/ext/iodine/iodine_fiobj2rb.h +0 -120
- data/ext/iodine/iodine_helpers.c +0 -282
- data/ext/iodine/iodine_helpers.h +0 -12
- data/ext/iodine/iodine_http.c +0 -1181
- data/ext/iodine/iodine_http.h +0 -23
- data/ext/iodine/iodine_json.c +0 -302
- data/ext/iodine/iodine_mustache.c +0 -567
- data/ext/iodine/iodine_pubsub.c +0 -580
- data/ext/iodine/iodine_pubsub.h +0 -26
- data/ext/iodine/iodine_rack_io.c +0 -273
- data/ext/iodine/iodine_rack_io.h +0 -20
- data/ext/iodine/iodine_store.c +0 -148
- data/ext/iodine/iodine_tcp.c +0 -346
- data/ext/iodine/iodine_tcp.h +0 -13
- data/ext/iodine/iodine_tls.c +0 -261
- data/ext/iodine/mustache_parser.h +0 -1546
- data/ext/iodine/redis_engine.c +0 -957
- data/ext/iodine/redis_engine.h +0 -79
- data/ext/iodine/resp_parser.h +0 -317
- data/ext/iodine/websocket_parser.h +0 -506
- data/ext/iodine/websockets.c +0 -743
- data/ext/iodine/websockets.h +0 -185
- data/lib/iodine/connection.rb +0 -61
- data/lib/iodine/json.rb +0 -42
- data/lib/iodine/mustache.rb +0 -113
- data/lib/iodine/pubsub.rb +0 -55
- data/lib/iodine/rack_utils.rb +0 -43
- data/lib/iodine/tls.rb +0 -16
- data/lib/rack/handler/iodine.rb +0 -33
- data/logo.png +0 -0
data/ext/iodine/websockets.c
DELETED
|
@@ -1,743 +0,0 @@
|
|
|
1
|
-
/*
|
|
2
|
-
copyright: Boaz Segev, 2016-2019
|
|
3
|
-
license: MIT
|
|
4
|
-
|
|
5
|
-
Feel free to copy, use and enjoy according to the license provided.
|
|
6
|
-
*/
|
|
7
|
-
#define FIO_INCLUDE_STR
|
|
8
|
-
#include <fio.h>
|
|
9
|
-
|
|
10
|
-
/* subscription lists have a long lifetime */
|
|
11
|
-
#define FIO_FORCE_MALLOC_TMP 1
|
|
12
|
-
#define FIO_INCLUDE_LINKED_LIST
|
|
13
|
-
#include <fio.h>
|
|
14
|
-
|
|
15
|
-
#include <fiobj.h>
|
|
16
|
-
|
|
17
|
-
#include <http.h>
|
|
18
|
-
#include <http_internal.h>
|
|
19
|
-
|
|
20
|
-
#ifndef __MINGW32__
|
|
21
|
-
#include <arpa/inet.h>
|
|
22
|
-
#endif
|
|
23
|
-
#include <errno.h>
|
|
24
|
-
#include <stdio.h>
|
|
25
|
-
#include <stdlib.h>
|
|
26
|
-
#include <string.h>
|
|
27
|
-
#include <strings.h>
|
|
28
|
-
|
|
29
|
-
#include <websocket_parser.h>
|
|
30
|
-
|
|
31
|
-
#if !defined(__BIG_ENDIAN__) && !defined(__LITTLE_ENDIAN__) && \
|
|
32
|
-
!defined(__MINGW32__)
|
|
33
|
-
#include <endian.h>
|
|
34
|
-
#if !defined(__BIG_ENDIAN__) && !defined(__LITTLE_ENDIAN__) && \
|
|
35
|
-
__BYTE_ORDER__ == __ORDER_BIG_ENDIAN__
|
|
36
|
-
#define __BIG_ENDIAN__
|
|
37
|
-
#endif
|
|
38
|
-
#endif
|
|
39
|
-
|
|
40
|
-
/*******************************************************************************
|
|
41
|
-
Buffer management - update to change the way the buffer is handled.
|
|
42
|
-
*/
|
|
43
|
-
struct buffer_s {
|
|
44
|
-
void *data;
|
|
45
|
-
size_t size;
|
|
46
|
-
};
|
|
47
|
-
|
|
48
|
-
#pragma weak create_ws_buffer
|
|
49
|
-
/** returns a buffer_s struct, with a buffer (at least) `size` long. */
|
|
50
|
-
struct buffer_s create_ws_buffer(ws_s *owner);
|
|
51
|
-
|
|
52
|
-
#pragma weak resize_ws_buffer
|
|
53
|
-
/** returns a buffer_s struct, with a buffer (at least) `size` long. */
|
|
54
|
-
struct buffer_s resize_ws_buffer(ws_s *owner, struct buffer_s);
|
|
55
|
-
|
|
56
|
-
#pragma weak free_ws_buffer
|
|
57
|
-
/** releases an existing buffer. */
|
|
58
|
-
void free_ws_buffer(ws_s *owner, struct buffer_s);
|
|
59
|
-
|
|
60
|
-
/** Sets the initial buffer size. (4Kb)*/
|
|
61
|
-
#define WS_INITIAL_BUFFER_SIZE 4096UL
|
|
62
|
-
|
|
63
|
-
/*******************************************************************************
|
|
64
|
-
Buffer management - simple implementation...
|
|
65
|
-
Since Websocket connections have a long life expectancy, optimizing this part of
|
|
66
|
-
the code probably wouldn't offer a high performance boost.
|
|
67
|
-
*/
|
|
68
|
-
|
|
69
|
-
// buffer increments by 4,096 Bytes (4Kb)
|
|
70
|
-
#define round_up_buffer_size(size) (((size) >> 12) + 1) << 12
|
|
71
|
-
|
|
72
|
-
struct buffer_s create_ws_buffer(ws_s *owner) {
|
|
73
|
-
(void)(owner);
|
|
74
|
-
struct buffer_s buff;
|
|
75
|
-
buff.size = WS_INITIAL_BUFFER_SIZE;
|
|
76
|
-
buff.data = malloc(buff.size);
|
|
77
|
-
FIO_ASSERT_ALLOC(buff.data);
|
|
78
|
-
return buff;
|
|
79
|
-
}
|
|
80
|
-
|
|
81
|
-
struct buffer_s resize_ws_buffer(ws_s *owner, struct buffer_s buff) {
|
|
82
|
-
buff.size = round_up_buffer_size(buff.size);
|
|
83
|
-
void *tmp = realloc(buff.data, buff.size);
|
|
84
|
-
if (!tmp) {
|
|
85
|
-
free_ws_buffer(owner, buff);
|
|
86
|
-
buff.size = 0;
|
|
87
|
-
}
|
|
88
|
-
buff.data = tmp;
|
|
89
|
-
return buff;
|
|
90
|
-
}
|
|
91
|
-
void free_ws_buffer(ws_s *owner, struct buffer_s buff) {
|
|
92
|
-
(void)(owner);
|
|
93
|
-
free(buff.data);
|
|
94
|
-
}
|
|
95
|
-
|
|
96
|
-
#undef round_up_buffer_size
|
|
97
|
-
|
|
98
|
-
/*******************************************************************************
|
|
99
|
-
Create/Destroy the websocket object (prototypes)
|
|
100
|
-
*/
|
|
101
|
-
|
|
102
|
-
static ws_s *new_websocket(intptr_t uuid);
|
|
103
|
-
static void destroy_ws(ws_s *ws);
|
|
104
|
-
|
|
105
|
-
/*******************************************************************************
|
|
106
|
-
The Websocket object (protocol + parser)
|
|
107
|
-
*/
|
|
108
|
-
struct ws_s {
|
|
109
|
-
/** The Websocket protocol */
|
|
110
|
-
fio_protocol_s protocol;
|
|
111
|
-
/** connection data */
|
|
112
|
-
intptr_t fd;
|
|
113
|
-
/** callbacks */
|
|
114
|
-
void (*on_message)(ws_s *ws, fio_str_info_s msg, uint8_t is_text);
|
|
115
|
-
void (*on_shutdown)(ws_s *ws);
|
|
116
|
-
void (*on_ready)(ws_s *ws);
|
|
117
|
-
void (*on_open)(ws_s *ws);
|
|
118
|
-
void (*on_close)(intptr_t uuid, void *udata);
|
|
119
|
-
/** Opaque user data. */
|
|
120
|
-
void *udata;
|
|
121
|
-
/** The maximum websocket message size */
|
|
122
|
-
size_t max_msg_size;
|
|
123
|
-
/** active pub/sub subscriptions */
|
|
124
|
-
fio_ls_s subscriptions;
|
|
125
|
-
fio_lock_i sub_lock;
|
|
126
|
-
/** socket buffer. */
|
|
127
|
-
struct buffer_s buffer;
|
|
128
|
-
/** data length (how much of the buffer actually used). */
|
|
129
|
-
size_t length;
|
|
130
|
-
/** total data length (including continuation frames). */
|
|
131
|
-
size_t total_length;
|
|
132
|
-
/** message buffer. */
|
|
133
|
-
FIOBJ msg;
|
|
134
|
-
/** latest text state. */
|
|
135
|
-
uint8_t is_text;
|
|
136
|
-
/** websocket connection type. */
|
|
137
|
-
uint8_t is_client;
|
|
138
|
-
};
|
|
139
|
-
|
|
140
|
-
/* *****************************************************************************
|
|
141
|
-
Create/Destroy the websocket subscription objects
|
|
142
|
-
***************************************************************************** */
|
|
143
|
-
|
|
144
|
-
static inline void clear_subscriptions(ws_s *ws) {
|
|
145
|
-
fio_lock(&ws->sub_lock);
|
|
146
|
-
while (fio_ls_any(&ws->subscriptions)) {
|
|
147
|
-
fio_unsubscribe(fio_ls_pop(&ws->subscriptions));
|
|
148
|
-
}
|
|
149
|
-
fio_unlock(&ws->sub_lock);
|
|
150
|
-
}
|
|
151
|
-
|
|
152
|
-
/* *****************************************************************************
|
|
153
|
-
Callbacks - Required functions for websocket_parser.h
|
|
154
|
-
***************************************************************************** */
|
|
155
|
-
|
|
156
|
-
static void websocket_on_unwrapped(void *ws_p, void *msg, uint64_t len,
|
|
157
|
-
char first, char last, char text,
|
|
158
|
-
unsigned char rsv) {
|
|
159
|
-
ws_s *ws = ws_p;
|
|
160
|
-
if (!ws)
|
|
161
|
-
return;
|
|
162
|
-
if (last && first) {
|
|
163
|
-
ws->on_message(ws, (fio_str_info_s){.data = msg, .len = len},
|
|
164
|
-
(uint8_t)text);
|
|
165
|
-
return;
|
|
166
|
-
}
|
|
167
|
-
if (ws->msg == FIOBJ_INVALID)
|
|
168
|
-
ws->msg = fiobj_str_buf(len);
|
|
169
|
-
ws->total_length += len;
|
|
170
|
-
if (first) {
|
|
171
|
-
ws->is_text = (uint8_t)text;
|
|
172
|
-
}
|
|
173
|
-
fiobj_str_write(ws->msg, msg, len);
|
|
174
|
-
if (last) {
|
|
175
|
-
ws->on_message(ws, fiobj_obj2cstr(ws->msg), ws->is_text);
|
|
176
|
-
fiobj_str_resize(ws->msg, 0);
|
|
177
|
-
ws->total_length = 0;
|
|
178
|
-
}
|
|
179
|
-
|
|
180
|
-
(void)rsv;
|
|
181
|
-
}
|
|
182
|
-
static void websocket_on_protocol_ping(void *ws_p, void *msg_, uint64_t len) {
|
|
183
|
-
ws_s *ws = ws_p;
|
|
184
|
-
if (msg_) {
|
|
185
|
-
void *buff = malloc(len + 16);
|
|
186
|
-
FIO_ASSERT_ALLOC(buff);
|
|
187
|
-
len = (((ws_s *)ws)->is_client
|
|
188
|
-
? websocket_client_wrap(buff, msg_, len, 10, 1, 1, 0)
|
|
189
|
-
: websocket_server_wrap(buff, msg_, len, 10, 1, 1, 0));
|
|
190
|
-
fio_write2(ws->fd, .data.buffer = buff, .length = len);
|
|
191
|
-
} else {
|
|
192
|
-
if (((ws_s *)ws)->is_client) {
|
|
193
|
-
fio_write2(ws->fd, .data.buffer = "\x89\x80mask", .length = 2,
|
|
194
|
-
.after.dealloc = FIO_DEALLOC_NOOP);
|
|
195
|
-
} else {
|
|
196
|
-
fio_write2(ws->fd, .data.buffer = "\x89\x00", .length = 2,
|
|
197
|
-
.after.dealloc = FIO_DEALLOC_NOOP);
|
|
198
|
-
}
|
|
199
|
-
}
|
|
200
|
-
}
|
|
201
|
-
static void websocket_on_protocol_pong(void *ws_p, void *msg, uint64_t len) {
|
|
202
|
-
(void)len;
|
|
203
|
-
(void)msg;
|
|
204
|
-
(void)ws_p;
|
|
205
|
-
}
|
|
206
|
-
static void websocket_on_protocol_close(void *ws_p) {
|
|
207
|
-
ws_s *ws = ws_p;
|
|
208
|
-
fio_close(ws->fd);
|
|
209
|
-
}
|
|
210
|
-
static void websocket_on_protocol_error(void *ws_p) {
|
|
211
|
-
ws_s *ws = ws_p;
|
|
212
|
-
fio_close(ws->fd);
|
|
213
|
-
}
|
|
214
|
-
|
|
215
|
-
/*******************************************************************************
|
|
216
|
-
The Websocket Protocol implementation
|
|
217
|
-
*/
|
|
218
|
-
|
|
219
|
-
#define ws_protocol(fd) ((ws_s *)(server_get_protocol(fd)))
|
|
220
|
-
|
|
221
|
-
static void ws_ping(intptr_t fd, fio_protocol_s *ws) {
|
|
222
|
-
(void)(ws);
|
|
223
|
-
if (((ws_s *)ws)->is_client) {
|
|
224
|
-
fio_write2(fd, .data.buffer = "\x89\x80MASK", .length = 6,
|
|
225
|
-
.after.dealloc = FIO_DEALLOC_NOOP);
|
|
226
|
-
} else {
|
|
227
|
-
fio_write2(fd, .data.buffer = "\x89\x00", .length = 2,
|
|
228
|
-
.after.dealloc = FIO_DEALLOC_NOOP);
|
|
229
|
-
}
|
|
230
|
-
FIO_LOG_DEBUG("Sent ping for Websocket %p (%d)", (void *)ws, (int)fd);
|
|
231
|
-
}
|
|
232
|
-
|
|
233
|
-
static void on_close(intptr_t uuid, fio_protocol_s *_ws) {
|
|
234
|
-
destroy_ws((ws_s *)_ws);
|
|
235
|
-
(void)uuid;
|
|
236
|
-
}
|
|
237
|
-
|
|
238
|
-
static void on_ready(intptr_t fduuid, fio_protocol_s *ws) {
|
|
239
|
-
(void)(fduuid);
|
|
240
|
-
if (((ws_s *)ws)->on_ready)
|
|
241
|
-
((ws_s *)ws)->on_ready((ws_s *)ws);
|
|
242
|
-
}
|
|
243
|
-
|
|
244
|
-
static uint8_t on_shutdown(intptr_t fd, fio_protocol_s *ws) {
|
|
245
|
-
(void)(fd);
|
|
246
|
-
if (ws && ((ws_s *)ws)->on_shutdown)
|
|
247
|
-
((ws_s *)ws)->on_shutdown((ws_s *)ws);
|
|
248
|
-
if (((ws_s *)ws)->is_client) {
|
|
249
|
-
fio_write2(fd, .data.buffer = "\x8a\x80MASK", .length = 6,
|
|
250
|
-
.after.dealloc = FIO_DEALLOC_NOOP);
|
|
251
|
-
} else {
|
|
252
|
-
fio_write2(fd, .data.buffer = "\x8a\x00", .length = 2,
|
|
253
|
-
.after.dealloc = FIO_DEALLOC_NOOP);
|
|
254
|
-
}
|
|
255
|
-
return 0;
|
|
256
|
-
}
|
|
257
|
-
|
|
258
|
-
static void on_data(intptr_t sockfd, fio_protocol_s *ws_) {
|
|
259
|
-
ws_s *const ws = (ws_s *)ws_;
|
|
260
|
-
if (ws == NULL)
|
|
261
|
-
return;
|
|
262
|
-
struct websocket_packet_info_s info =
|
|
263
|
-
websocket_buffer_peek(ws->buffer.data, ws->length);
|
|
264
|
-
const uint64_t raw_length = info.packet_length + info.head_length;
|
|
265
|
-
/* test expected data amount */
|
|
266
|
-
if (ws->max_msg_size < raw_length + ws->total_length) {
|
|
267
|
-
/* too big */
|
|
268
|
-
websocket_close(ws);
|
|
269
|
-
return;
|
|
270
|
-
}
|
|
271
|
-
/* test buffer capacity */
|
|
272
|
-
if (raw_length > ws->buffer.size) {
|
|
273
|
-
ws->buffer.size = (size_t)raw_length;
|
|
274
|
-
ws->buffer = resize_ws_buffer(ws, ws->buffer);
|
|
275
|
-
if (!ws->buffer.data) {
|
|
276
|
-
// no memory.
|
|
277
|
-
websocket_close(ws);
|
|
278
|
-
return;
|
|
279
|
-
}
|
|
280
|
-
}
|
|
281
|
-
|
|
282
|
-
const ssize_t len = fio_read(sockfd, (uint8_t *)ws->buffer.data + ws->length,
|
|
283
|
-
ws->buffer.size - ws->length);
|
|
284
|
-
if (len <= 0) {
|
|
285
|
-
return;
|
|
286
|
-
}
|
|
287
|
-
ws->length = websocket_consume(ws->buffer.data, ws->length + len, ws,
|
|
288
|
-
(~(ws->is_client) & 1));
|
|
289
|
-
|
|
290
|
-
fio_force_event(sockfd, FIO_EVENT_ON_DATA);
|
|
291
|
-
}
|
|
292
|
-
|
|
293
|
-
static void on_data_first(intptr_t sockfd, fio_protocol_s *ws_) {
|
|
294
|
-
ws_s *const ws = (ws_s *)ws_;
|
|
295
|
-
if (ws->on_open)
|
|
296
|
-
ws->on_open(ws);
|
|
297
|
-
ws->protocol.on_data = on_data;
|
|
298
|
-
ws->protocol.on_ready = on_ready;
|
|
299
|
-
|
|
300
|
-
if (ws->length) {
|
|
301
|
-
ws->length = websocket_consume(ws->buffer.data, ws->length, ws,
|
|
302
|
-
(~(ws->is_client) & 1));
|
|
303
|
-
}
|
|
304
|
-
fio_force_event(sockfd, FIO_EVENT_ON_DATA);
|
|
305
|
-
fio_force_event(sockfd, FIO_EVENT_ON_READY);
|
|
306
|
-
}
|
|
307
|
-
|
|
308
|
-
/* later */
|
|
309
|
-
static void websocket_write_impl(intptr_t fd, void *data, size_t len, char text,
|
|
310
|
-
char first, char last, char client);
|
|
311
|
-
|
|
312
|
-
/*******************************************************************************
|
|
313
|
-
Create/Destroy the websocket object
|
|
314
|
-
*/
|
|
315
|
-
|
|
316
|
-
static ws_s *new_websocket(intptr_t uuid) {
|
|
317
|
-
// allocate the protocol object
|
|
318
|
-
ws_s *ws = malloc(sizeof(*ws));
|
|
319
|
-
FIO_ASSERT_ALLOC(ws);
|
|
320
|
-
*ws = (ws_s){
|
|
321
|
-
.protocol.ping = ws_ping,
|
|
322
|
-
.protocol.on_data = on_data_first,
|
|
323
|
-
.protocol.on_close = on_close,
|
|
324
|
-
.protocol.on_ready = NULL /* filled in after `on_open` */,
|
|
325
|
-
.protocol.on_shutdown = on_shutdown,
|
|
326
|
-
.subscriptions = FIO_LS_INIT(ws->subscriptions),
|
|
327
|
-
.is_client = 0,
|
|
328
|
-
.fd = uuid,
|
|
329
|
-
};
|
|
330
|
-
return ws;
|
|
331
|
-
}
|
|
332
|
-
static void destroy_ws(ws_s *ws) {
|
|
333
|
-
if (ws->on_close)
|
|
334
|
-
ws->on_close(ws->fd, ws->udata);
|
|
335
|
-
if (ws->msg)
|
|
336
|
-
fiobj_free(ws->msg);
|
|
337
|
-
clear_subscriptions(ws);
|
|
338
|
-
free_ws_buffer(ws, ws->buffer);
|
|
339
|
-
free(ws);
|
|
340
|
-
}
|
|
341
|
-
|
|
342
|
-
void websocket_attach(intptr_t uuid, http_settings_s *http_settings,
|
|
343
|
-
websocket_settings_s *args, void *data, size_t length) {
|
|
344
|
-
ws_s *ws = new_websocket(uuid);
|
|
345
|
-
// we have an active websocket connection - prep the connection buffer
|
|
346
|
-
ws->buffer = create_ws_buffer(ws);
|
|
347
|
-
// Setup ws callbacks
|
|
348
|
-
ws->on_open = args->on_open;
|
|
349
|
-
ws->on_close = args->on_close;
|
|
350
|
-
ws->on_message = args->on_message;
|
|
351
|
-
ws->on_ready = args->on_ready;
|
|
352
|
-
ws->on_shutdown = args->on_shutdown;
|
|
353
|
-
// setup any user data
|
|
354
|
-
ws->udata = args->udata;
|
|
355
|
-
if (http_settings) {
|
|
356
|
-
// client mode?
|
|
357
|
-
ws->is_client = http_settings->is_client;
|
|
358
|
-
// buffer limits
|
|
359
|
-
ws->max_msg_size = http_settings->ws_max_msg_size;
|
|
360
|
-
// update the timeout
|
|
361
|
-
fio_timeout_set(uuid, http_settings->ws_timeout);
|
|
362
|
-
} else {
|
|
363
|
-
ws->max_msg_size = (1024 * 256);
|
|
364
|
-
fio_timeout_set(uuid, 40);
|
|
365
|
-
}
|
|
366
|
-
|
|
367
|
-
if (data && length) {
|
|
368
|
-
if (length > ws->buffer.size) {
|
|
369
|
-
ws->buffer.size = length;
|
|
370
|
-
ws->buffer = resize_ws_buffer(ws, ws->buffer);
|
|
371
|
-
if (!ws->buffer.data) {
|
|
372
|
-
// no memory.
|
|
373
|
-
fio_attach(uuid, (fio_protocol_s *)ws);
|
|
374
|
-
websocket_close(ws);
|
|
375
|
-
return;
|
|
376
|
-
}
|
|
377
|
-
}
|
|
378
|
-
memcpy(ws->buffer.data, data, length);
|
|
379
|
-
ws->length = length;
|
|
380
|
-
}
|
|
381
|
-
// update the protocol object, cleaning up the old one
|
|
382
|
-
fio_attach(uuid, (fio_protocol_s *)ws);
|
|
383
|
-
// allow the on_open and on_data to take over the control.
|
|
384
|
-
fio_force_event(uuid, FIO_EVENT_ON_DATA);
|
|
385
|
-
}
|
|
386
|
-
|
|
387
|
-
/*******************************************************************************
|
|
388
|
-
Writing to the Websocket
|
|
389
|
-
*/
|
|
390
|
-
#define WS_MAX_FRAME_SIZE \
|
|
391
|
-
(FIO_MEMORY_BLOCK_ALLOC_LIMIT - 4096) // should be less then `unsigned short`
|
|
392
|
-
|
|
393
|
-
static void websocket_write_impl(intptr_t fd, void *data, size_t len, char text,
|
|
394
|
-
char first, char last, char client) {
|
|
395
|
-
if (len <= WS_MAX_FRAME_SIZE) {
|
|
396
|
-
void *buff = fio_malloc(len + 16);
|
|
397
|
-
FIO_ASSERT_ALLOC(buff);
|
|
398
|
-
len = (client ? websocket_client_wrap(buff, data, len, (text ? 1 : 2),
|
|
399
|
-
first, last, 0)
|
|
400
|
-
: websocket_server_wrap(buff, data, len, (text ? 1 : 2),
|
|
401
|
-
first, last, 0));
|
|
402
|
-
fio_write2(fd, .data.buffer = buff, .length = len,
|
|
403
|
-
.after.dealloc = fio_free);
|
|
404
|
-
} else {
|
|
405
|
-
/* frame fragmentation is better for large data then large frames */
|
|
406
|
-
while (len > WS_MAX_FRAME_SIZE) {
|
|
407
|
-
websocket_write_impl(fd, data, WS_MAX_FRAME_SIZE, text, first, 0, client);
|
|
408
|
-
data = ((uint8_t *)data) + WS_MAX_FRAME_SIZE;
|
|
409
|
-
first = 0;
|
|
410
|
-
len -= WS_MAX_FRAME_SIZE;
|
|
411
|
-
}
|
|
412
|
-
websocket_write_impl(fd, data, len, text, first, 1, client);
|
|
413
|
-
}
|
|
414
|
-
return;
|
|
415
|
-
}
|
|
416
|
-
|
|
417
|
-
/* *****************************************************************************
|
|
418
|
-
Multi-client broadcast optimizations
|
|
419
|
-
***************************************************************************** */
|
|
420
|
-
|
|
421
|
-
static void websocket_optimize_free(fio_msg_s *msg, void *metadata) {
|
|
422
|
-
fiobj_free((FIOBJ)metadata);
|
|
423
|
-
(void)msg;
|
|
424
|
-
}
|
|
425
|
-
|
|
426
|
-
static inline fio_msg_metadata_s websocket_optimize(fio_str_info_s msg,
|
|
427
|
-
unsigned char opcode) {
|
|
428
|
-
FIOBJ out = fiobj_str_buf(msg.len + 10);
|
|
429
|
-
fiobj_str_resize(out,
|
|
430
|
-
websocket_server_wrap(fiobj_obj2cstr(out).data, msg.data,
|
|
431
|
-
msg.len, opcode, 1, 1, 0));
|
|
432
|
-
fio_msg_metadata_s ret = {
|
|
433
|
-
.on_finish = websocket_optimize_free,
|
|
434
|
-
.metadata = (void *)out,
|
|
435
|
-
};
|
|
436
|
-
return ret;
|
|
437
|
-
}
|
|
438
|
-
static fio_msg_metadata_s websocket_optimize_generic(fio_str_info_s ch,
|
|
439
|
-
fio_str_info_s msg,
|
|
440
|
-
uint8_t is_json) {
|
|
441
|
-
fio_str_s tmp = FIO_STR_INIT_EXISTING(ch.data, ch.len, 0); // don't free
|
|
442
|
-
tmp.dealloc = NULL;
|
|
443
|
-
unsigned char opcode = 2;
|
|
444
|
-
if (tmp.len <= (2 << 19) && fio_str_utf8_valid(&tmp)) {
|
|
445
|
-
opcode = 1;
|
|
446
|
-
}
|
|
447
|
-
fio_msg_metadata_s ret = websocket_optimize(msg, opcode);
|
|
448
|
-
ret.type_id = WEBSOCKET_OPTIMIZE_PUBSUB;
|
|
449
|
-
return ret;
|
|
450
|
-
(void)ch;
|
|
451
|
-
(void)is_json;
|
|
452
|
-
}
|
|
453
|
-
|
|
454
|
-
static fio_msg_metadata_s websocket_optimize_text(fio_str_info_s ch,
|
|
455
|
-
fio_str_info_s msg,
|
|
456
|
-
uint8_t is_json) {
|
|
457
|
-
fio_msg_metadata_s ret = websocket_optimize(msg, 1);
|
|
458
|
-
ret.type_id = WEBSOCKET_OPTIMIZE_PUBSUB_TEXT;
|
|
459
|
-
return ret;
|
|
460
|
-
(void)ch;
|
|
461
|
-
(void)is_json;
|
|
462
|
-
}
|
|
463
|
-
|
|
464
|
-
static fio_msg_metadata_s websocket_optimize_binary(fio_str_info_s ch,
|
|
465
|
-
fio_str_info_s msg,
|
|
466
|
-
uint8_t is_json) {
|
|
467
|
-
fio_msg_metadata_s ret = websocket_optimize(msg, 2);
|
|
468
|
-
ret.type_id = WEBSOCKET_OPTIMIZE_PUBSUB_BINARY;
|
|
469
|
-
return ret;
|
|
470
|
-
(void)ch;
|
|
471
|
-
(void)is_json;
|
|
472
|
-
}
|
|
473
|
-
|
|
474
|
-
/**
|
|
475
|
-
* Enables (or disables) broadcast optimizations.
|
|
476
|
-
*
|
|
477
|
-
* When using WebSocket pub/sub system is originally optimized for either
|
|
478
|
-
* non-direct transmission (messages are handled by callbacks) or direct
|
|
479
|
-
* transmission to 1-3 clients per channel (on average), meaning that the
|
|
480
|
-
* majority of the messages are meant for a single recipient (or multiple
|
|
481
|
-
* callback recipients) and only some are expected to be directly transmitted to
|
|
482
|
-
* a group.
|
|
483
|
-
*
|
|
484
|
-
* However, when most messages are intended for direct transmission to more than
|
|
485
|
-
* 3 clients (on average), certain optimizations can be made to improve memory
|
|
486
|
-
* consumption (minimize duplication or WebSocket network data).
|
|
487
|
-
*
|
|
488
|
-
* This function allows enablement (or disablement) of these optimizations.
|
|
489
|
-
* These optimizations include:
|
|
490
|
-
*
|
|
491
|
-
* * WEBSOCKET_OPTIMIZE_PUBSUB - optimize all direct transmission messages,
|
|
492
|
-
* best attempt to detect Text vs. Binary data.
|
|
493
|
-
* * WEBSOCKET_OPTIMIZE_PUBSUB_TEXT - optimize direct pub/sub text messages.
|
|
494
|
-
* * WEBSOCKET_OPTIMIZE_PUBSUB_BINARY - optimize direct pub/sub binary messages.
|
|
495
|
-
*
|
|
496
|
-
* Note: to disable an optimization it should be disabled the same amount of
|
|
497
|
-
* times it was enabled - multiple optimization enablements for the same type
|
|
498
|
-
* are merged, but reference counted (disabled when reference is zero).
|
|
499
|
-
*/
|
|
500
|
-
void websocket_optimize4broadcasts(intptr_t type, int enable) {
|
|
501
|
-
static intptr_t generic = 0;
|
|
502
|
-
static intptr_t text = 0;
|
|
503
|
-
static intptr_t binary = 0;
|
|
504
|
-
fio_msg_metadata_s (*callback)(fio_str_info_s, fio_str_info_s, uint8_t);
|
|
505
|
-
intptr_t *counter;
|
|
506
|
-
switch ((0 - type)) {
|
|
507
|
-
case (0 - WEBSOCKET_OPTIMIZE_PUBSUB):
|
|
508
|
-
counter = &generic;
|
|
509
|
-
callback = websocket_optimize_generic;
|
|
510
|
-
break;
|
|
511
|
-
case (0 - WEBSOCKET_OPTIMIZE_PUBSUB_TEXT):
|
|
512
|
-
counter = &text;
|
|
513
|
-
callback = websocket_optimize_text;
|
|
514
|
-
break;
|
|
515
|
-
case (0 - WEBSOCKET_OPTIMIZE_PUBSUB_BINARY):
|
|
516
|
-
counter = &binary;
|
|
517
|
-
callback = websocket_optimize_binary;
|
|
518
|
-
break;
|
|
519
|
-
default:
|
|
520
|
-
return;
|
|
521
|
-
}
|
|
522
|
-
if (enable) {
|
|
523
|
-
if (fio_atomic_add(counter, 1) == 1) {
|
|
524
|
-
fio_message_metadata_callback_set(callback, 1);
|
|
525
|
-
}
|
|
526
|
-
} else {
|
|
527
|
-
if (fio_atomic_sub(counter, 1) == 0) {
|
|
528
|
-
fio_message_metadata_callback_set(callback, 0);
|
|
529
|
-
}
|
|
530
|
-
}
|
|
531
|
-
}
|
|
532
|
-
|
|
533
|
-
/* *****************************************************************************
|
|
534
|
-
Subscription handling
|
|
535
|
-
***************************************************************************** */
|
|
536
|
-
|
|
537
|
-
typedef struct {
|
|
538
|
-
void (*on_message)(ws_s *ws, fio_str_info_s channel, fio_str_info_s msg,
|
|
539
|
-
void *udata);
|
|
540
|
-
void (*on_unsubscribe)(void *udata);
|
|
541
|
-
void *udata;
|
|
542
|
-
} websocket_sub_data_s;
|
|
543
|
-
|
|
544
|
-
static inline void websocket_on_pubsub_message_direct_internal(fio_msg_s *msg,
|
|
545
|
-
uint8_t txt) {
|
|
546
|
-
fio_protocol_s *pr =
|
|
547
|
-
fio_protocol_try_lock((intptr_t)msg->udata1, FIO_PR_LOCK_WRITE);
|
|
548
|
-
if (!pr) {
|
|
549
|
-
if (errno == EBADF)
|
|
550
|
-
return;
|
|
551
|
-
fio_message_defer(msg);
|
|
552
|
-
return;
|
|
553
|
-
}
|
|
554
|
-
FIOBJ message = FIOBJ_INVALID;
|
|
555
|
-
FIOBJ pre_wrapped = FIOBJ_INVALID;
|
|
556
|
-
if (!((ws_s *)pr)->is_client) {
|
|
557
|
-
/* pre-wrapping is only for client data */
|
|
558
|
-
switch (txt) {
|
|
559
|
-
case 0:
|
|
560
|
-
pre_wrapped =
|
|
561
|
-
(FIOBJ)fio_message_metadata(msg, WEBSOCKET_OPTIMIZE_PUBSUB_BINARY);
|
|
562
|
-
break;
|
|
563
|
-
case 1:
|
|
564
|
-
pre_wrapped =
|
|
565
|
-
(FIOBJ)fio_message_metadata(msg, WEBSOCKET_OPTIMIZE_PUBSUB_TEXT);
|
|
566
|
-
break;
|
|
567
|
-
case 2:
|
|
568
|
-
pre_wrapped = (FIOBJ)fio_message_metadata(msg, WEBSOCKET_OPTIMIZE_PUBSUB);
|
|
569
|
-
break;
|
|
570
|
-
default:
|
|
571
|
-
break;
|
|
572
|
-
}
|
|
573
|
-
if (pre_wrapped) {
|
|
574
|
-
// FIO_LOG_DEBUG(
|
|
575
|
-
// "pub/sub WebSocket optimization route for pre-wrapped message.");
|
|
576
|
-
fiobj_send_free((intptr_t)msg->udata1, fiobj_dup(pre_wrapped));
|
|
577
|
-
goto finish;
|
|
578
|
-
}
|
|
579
|
-
}
|
|
580
|
-
if (txt == 2) {
|
|
581
|
-
/* unknown text state */
|
|
582
|
-
fio_str_s tmp =
|
|
583
|
-
FIO_STR_INIT_STATIC2(msg->msg.data, msg->msg.len); // don't free
|
|
584
|
-
txt = (tmp.len >= (2 << 14) ? 0 : fio_str_utf8_valid(&tmp));
|
|
585
|
-
}
|
|
586
|
-
websocket_write((ws_s *)pr, msg->msg, txt & 1);
|
|
587
|
-
fiobj_free(message);
|
|
588
|
-
finish:
|
|
589
|
-
fio_protocol_unlock(pr, FIO_PR_LOCK_WRITE);
|
|
590
|
-
}
|
|
591
|
-
|
|
592
|
-
static void websocket_on_pubsub_message_direct(fio_msg_s *msg) {
|
|
593
|
-
websocket_on_pubsub_message_direct_internal(msg, 2);
|
|
594
|
-
}
|
|
595
|
-
|
|
596
|
-
static void websocket_on_pubsub_message_direct_txt(fio_msg_s *msg) {
|
|
597
|
-
websocket_on_pubsub_message_direct_internal(msg, 1);
|
|
598
|
-
}
|
|
599
|
-
|
|
600
|
-
static void websocket_on_pubsub_message_direct_bin(fio_msg_s *msg) {
|
|
601
|
-
websocket_on_pubsub_message_direct_internal(msg, 0);
|
|
602
|
-
}
|
|
603
|
-
|
|
604
|
-
static void websocket_on_pubsub_message(fio_msg_s *msg) {
|
|
605
|
-
fio_protocol_s *pr =
|
|
606
|
-
fio_protocol_try_lock((intptr_t)msg->udata1, FIO_PR_LOCK_TASK);
|
|
607
|
-
if (!pr) {
|
|
608
|
-
if (errno == EBADF)
|
|
609
|
-
return;
|
|
610
|
-
fio_message_defer(msg);
|
|
611
|
-
return;
|
|
612
|
-
}
|
|
613
|
-
websocket_sub_data_s *d = msg->udata2;
|
|
614
|
-
|
|
615
|
-
if (d->on_message)
|
|
616
|
-
d->on_message((ws_s *)pr, msg->channel, msg->msg, d->udata);
|
|
617
|
-
fio_protocol_unlock(pr, FIO_PR_LOCK_TASK);
|
|
618
|
-
}
|
|
619
|
-
|
|
620
|
-
static void websocket_on_unsubscribe(void *u1, void *u2) {
|
|
621
|
-
websocket_sub_data_s *d = u2;
|
|
622
|
-
if (d->on_unsubscribe) {
|
|
623
|
-
d->on_unsubscribe(d->udata);
|
|
624
|
-
}
|
|
625
|
-
|
|
626
|
-
if ((intptr_t)d->on_message == (intptr_t)WEBSOCKET_OPTIMIZE_PUBSUB) {
|
|
627
|
-
websocket_optimize4broadcasts(WEBSOCKET_OPTIMIZE_PUBSUB, 0);
|
|
628
|
-
} else if ((intptr_t)d->on_message ==
|
|
629
|
-
(intptr_t)WEBSOCKET_OPTIMIZE_PUBSUB_TEXT) {
|
|
630
|
-
websocket_optimize4broadcasts(WEBSOCKET_OPTIMIZE_PUBSUB_TEXT, 0);
|
|
631
|
-
} else if ((intptr_t)d->on_message ==
|
|
632
|
-
(intptr_t)WEBSOCKET_OPTIMIZE_PUBSUB_BINARY) {
|
|
633
|
-
websocket_optimize4broadcasts(WEBSOCKET_OPTIMIZE_PUBSUB_BINARY, 0);
|
|
634
|
-
}
|
|
635
|
-
free(d);
|
|
636
|
-
(void)u1;
|
|
637
|
-
}
|
|
638
|
-
|
|
639
|
-
/**
|
|
640
|
-
* Returns a subscription ID on success and 0 on failure.
|
|
641
|
-
*/
|
|
642
|
-
#undef websocket_subscribe
|
|
643
|
-
uintptr_t websocket_subscribe(struct websocket_subscribe_s args) {
|
|
644
|
-
if (!args.ws || !fio_is_valid(args.ws->fd))
|
|
645
|
-
goto error;
|
|
646
|
-
websocket_sub_data_s *d = malloc(sizeof(*d));
|
|
647
|
-
FIO_ASSERT_ALLOC(d);
|
|
648
|
-
*d = (websocket_sub_data_s){
|
|
649
|
-
.udata = args.udata,
|
|
650
|
-
.on_message = args.on_message,
|
|
651
|
-
.on_unsubscribe = args.on_unsubscribe,
|
|
652
|
-
};
|
|
653
|
-
void (*handler)(fio_msg_s *) = websocket_on_pubsub_message;
|
|
654
|
-
if (!args.on_message) {
|
|
655
|
-
intptr_t br_type;
|
|
656
|
-
if (args.force_binary) {
|
|
657
|
-
br_type = WEBSOCKET_OPTIMIZE_PUBSUB_BINARY;
|
|
658
|
-
handler = websocket_on_pubsub_message_direct_bin;
|
|
659
|
-
} else if (args.force_text) {
|
|
660
|
-
br_type = WEBSOCKET_OPTIMIZE_PUBSUB_TEXT;
|
|
661
|
-
handler = websocket_on_pubsub_message_direct_txt;
|
|
662
|
-
} else {
|
|
663
|
-
br_type = WEBSOCKET_OPTIMIZE_PUBSUB;
|
|
664
|
-
handler = websocket_on_pubsub_message_direct;
|
|
665
|
-
}
|
|
666
|
-
websocket_optimize4broadcasts(br_type, 1);
|
|
667
|
-
d->on_message =
|
|
668
|
-
(void (*)(ws_s *, fio_str_info_s, fio_str_info_s, void *))br_type;
|
|
669
|
-
}
|
|
670
|
-
subscription_s *sub =
|
|
671
|
-
fio_subscribe(.channel = args.channel, .match = args.match,
|
|
672
|
-
.on_unsubscribe = websocket_on_unsubscribe,
|
|
673
|
-
.on_message = handler, .udata1 = (void *)args.ws->fd,
|
|
674
|
-
.udata2 = d);
|
|
675
|
-
if (!sub) {
|
|
676
|
-
/* don't free `d`, return (`d` freed by fio_subscribe) */
|
|
677
|
-
return 0;
|
|
678
|
-
}
|
|
679
|
-
fio_ls_s *pos;
|
|
680
|
-
fio_lock(&args.ws->sub_lock);
|
|
681
|
-
pos = fio_ls_push(&args.ws->subscriptions, sub);
|
|
682
|
-
fio_unlock(&args.ws->sub_lock);
|
|
683
|
-
|
|
684
|
-
return (uintptr_t)pos;
|
|
685
|
-
error:
|
|
686
|
-
if (args.on_unsubscribe)
|
|
687
|
-
args.on_unsubscribe(args.udata);
|
|
688
|
-
return 0;
|
|
689
|
-
}
|
|
690
|
-
|
|
691
|
-
/**
|
|
692
|
-
* Unsubscribes from a channel.
|
|
693
|
-
*/
|
|
694
|
-
void websocket_unsubscribe(ws_s *ws, uintptr_t subscription_id) {
|
|
695
|
-
fio_unsubscribe((subscription_s *)((fio_ls_s *)subscription_id)->obj);
|
|
696
|
-
fio_lock(&ws->sub_lock);
|
|
697
|
-
fio_ls_remove((fio_ls_s *)subscription_id);
|
|
698
|
-
fio_unlock(&ws->sub_lock);
|
|
699
|
-
|
|
700
|
-
(void)ws;
|
|
701
|
-
}
|
|
702
|
-
|
|
703
|
-
/*******************************************************************************
|
|
704
|
-
The API implementation
|
|
705
|
-
*/
|
|
706
|
-
|
|
707
|
-
/** Returns the opaque user data associated with the websocket. */
|
|
708
|
-
void *websocket_udata_get(ws_s *ws) { return ws->udata; }
|
|
709
|
-
|
|
710
|
-
/** Returns the the process specific connection's UUID (see `libsock`). */
|
|
711
|
-
intptr_t websocket_uuid(ws_s *ws) { return ws->fd; }
|
|
712
|
-
|
|
713
|
-
/** Sets the opaque user data associated with the websocket.
|
|
714
|
-
* Returns the old value, if any. */
|
|
715
|
-
void *websocket_udata_set(ws_s *ws, void *udata) {
|
|
716
|
-
void *old = ws->udata;
|
|
717
|
-
ws->udata = udata;
|
|
718
|
-
return old;
|
|
719
|
-
}
|
|
720
|
-
|
|
721
|
-
/**
|
|
722
|
-
* Returns 1 if the WebSocket connection is in Client mode (connected to a
|
|
723
|
-
* remote server) and 0 if the connection is in Server mode (a connection
|
|
724
|
-
* established using facil.io's HTTP server).
|
|
725
|
-
*/
|
|
726
|
-
uint8_t websocket_is_client(ws_s *ws) { return ws->is_client; }
|
|
727
|
-
|
|
728
|
-
/** Writes data to the websocket. Returns -1 on failure (0 on success). */
|
|
729
|
-
int websocket_write(ws_s *ws, fio_str_info_s msg, uint8_t is_text) {
|
|
730
|
-
if (fio_is_valid(ws->fd)) {
|
|
731
|
-
websocket_write_impl(ws->fd, msg.data, msg.len, is_text, 1, 1,
|
|
732
|
-
ws->is_client);
|
|
733
|
-
return 0;
|
|
734
|
-
}
|
|
735
|
-
return -1;
|
|
736
|
-
}
|
|
737
|
-
/** Closes a websocket connection. */
|
|
738
|
-
void websocket_close(ws_s *ws) {
|
|
739
|
-
fio_write2(ws->fd, .data.buffer = "\x88\x00", .length = 2,
|
|
740
|
-
.after.dealloc = FIO_DEALLOC_NOOP);
|
|
741
|
-
fio_close(ws->fd);
|
|
742
|
-
return;
|
|
743
|
-
}
|