iodine 0.6.5 → 0.7.0
Sign up to get free protection for your applications and to get access to all the features.
Potentially problematic release.
This version of iodine might be problematic. Click here for more details.
- checksums.yaml +4 -4
- data/CHANGELOG.md +11 -0
- data/README.md +4 -4
- data/SPEC-Websocket-Draft.md +3 -6
- data/bin/mustache.rb +128 -0
- data/examples/test_template.mustache +16 -0
- data/ext/iodine/fio.c +9397 -0
- data/ext/iodine/fio.h +4723 -0
- data/ext/iodine/fio_ary.h +353 -54
- data/ext/iodine/fio_cli.c +351 -361
- data/ext/iodine/fio_cli.h +84 -105
- data/ext/iodine/fio_hashmap.h +70 -16
- data/ext/iodine/fio_json_parser.h +35 -24
- data/ext/iodine/fio_siphash.c +104 -4
- data/ext/iodine/fio_siphash.h +18 -2
- data/ext/iodine/fio_str.h +1218 -0
- data/ext/iodine/fio_tmpfile.h +1 -1
- data/ext/iodine/fiobj.h +13 -8
- data/ext/iodine/fiobj4sock.h +6 -8
- data/ext/iodine/fiobj_ary.c +107 -17
- data/ext/iodine/fiobj_ary.h +36 -4
- data/ext/iodine/fiobj_data.c +146 -127
- data/ext/iodine/fiobj_data.h +25 -23
- data/ext/iodine/fiobj_hash.c +7 -7
- data/ext/iodine/fiobj_hash.h +6 -5
- data/ext/iodine/fiobj_json.c +20 -17
- data/ext/iodine/fiobj_json.h +5 -5
- data/ext/iodine/fiobj_mem.h +71 -0
- data/ext/iodine/fiobj_mustache.c +310 -0
- data/ext/iodine/fiobj_mustache.h +40 -0
- data/ext/iodine/fiobj_numbers.c +199 -94
- data/ext/iodine/fiobj_numbers.h +7 -7
- data/ext/iodine/fiobj_str.c +142 -333
- data/ext/iodine/fiobj_str.h +65 -55
- data/ext/iodine/fiobject.c +49 -11
- data/ext/iodine/fiobject.h +40 -39
- data/ext/iodine/http.c +382 -190
- data/ext/iodine/http.h +124 -80
- data/ext/iodine/http1.c +99 -127
- data/ext/iodine/http1.h +5 -5
- data/ext/iodine/http1_parser.c +3 -2
- data/ext/iodine/http1_parser.h +2 -2
- data/ext/iodine/http_internal.c +14 -12
- data/ext/iodine/http_internal.h +25 -19
- data/ext/iodine/iodine.c +37 -18
- data/ext/iodine/iodine.h +4 -0
- data/ext/iodine/iodine_caller.c +9 -2
- data/ext/iodine/iodine_caller.h +2 -0
- data/ext/iodine/iodine_connection.c +82 -117
- data/ext/iodine/iodine_defer.c +57 -50
- data/ext/iodine/iodine_defer.h +0 -1
- data/ext/iodine/iodine_fiobj2rb.h +4 -2
- data/ext/iodine/iodine_helpers.c +4 -4
- data/ext/iodine/iodine_http.c +25 -32
- data/ext/iodine/iodine_json.c +2 -1
- data/ext/iodine/iodine_mustache.c +423 -0
- data/ext/iodine/iodine_mustache.h +6 -0
- data/ext/iodine/iodine_pubsub.c +48 -153
- data/ext/iodine/iodine_pubsub.h +5 -4
- data/ext/iodine/iodine_rack_io.c +7 -5
- data/ext/iodine/iodine_store.c +16 -13
- data/ext/iodine/iodine_tcp.c +26 -34
- data/ext/iodine/mustache_parser.h +1085 -0
- data/ext/iodine/redis_engine.c +740 -646
- data/ext/iodine/redis_engine.h +13 -15
- data/ext/iodine/resp_parser.h +11 -5
- data/ext/iodine/websocket_parser.h +13 -13
- data/ext/iodine/websockets.c +240 -393
- data/ext/iodine/websockets.h +52 -113
- data/lib/iodine.rb +1 -1
- data/lib/iodine/mustache.rb +140 -0
- data/lib/iodine/version.rb +1 -1
- metadata +15 -28
- data/ext/iodine/defer.c +0 -566
- data/ext/iodine/defer.h +0 -148
- data/ext/iodine/evio.c +0 -26
- data/ext/iodine/evio.h +0 -161
- data/ext/iodine/evio_callbacks.c +0 -26
- data/ext/iodine/evio_epoll.c +0 -251
- data/ext/iodine/evio_kqueue.c +0 -194
- data/ext/iodine/facil.c +0 -2325
- data/ext/iodine/facil.h +0 -616
- data/ext/iodine/fio_base64.c +0 -277
- data/ext/iodine/fio_base64.h +0 -71
- data/ext/iodine/fio_llist.h +0 -257
- data/ext/iodine/fio_mem.c +0 -675
- data/ext/iodine/fio_mem.h +0 -143
- data/ext/iodine/fio_random.c +0 -248
- data/ext/iodine/fio_random.h +0 -45
- data/ext/iodine/fio_sha1.c +0 -362
- data/ext/iodine/fio_sha1.h +0 -107
- data/ext/iodine/fio_sha2.c +0 -842
- data/ext/iodine/fio_sha2.h +0 -169
- data/ext/iodine/pubsub.c +0 -867
- data/ext/iodine/pubsub.h +0 -221
- data/ext/iodine/sock.c +0 -1366
- data/ext/iodine/sock.h +0 -566
- data/ext/iodine/spnlock.inc +0 -111
data/ext/iodine/redis_engine.h
CHANGED
@@ -7,8 +7,8 @@ Feel free to copy, use and enjoy according to the license provided.
|
|
7
7
|
#ifndef H_REDIS_ENGINE_H
|
8
8
|
#define H_REDIS_ENGINE_H
|
9
9
|
|
10
|
-
#include
|
11
|
-
#include
|
10
|
+
#include <fio.h>
|
11
|
+
#include <fiobj.h>
|
12
12
|
|
13
13
|
/* support C++ */
|
14
14
|
#ifdef __cplusplus
|
@@ -17,20 +17,18 @@ extern "C" {
|
|
17
17
|
|
18
18
|
/** possible arguments for the `redis_engine_create` function call */
|
19
19
|
struct redis_engine_create_args {
|
20
|
-
/** Redis server's address */
|
21
|
-
|
22
|
-
/** Redis server's port */
|
23
|
-
|
24
|
-
/** Redis server's password, if any */
|
25
|
-
|
26
|
-
/** Redis server's password length (if any). */
|
27
|
-
size_t auth_len;
|
20
|
+
/** Redis server's address, defaults to localhost. */
|
21
|
+
fio_str_info_s address;
|
22
|
+
/** Redis server's port, defaults to 6379. */
|
23
|
+
fio_str_info_s port;
|
24
|
+
/** Redis server's password, if any. */
|
25
|
+
fio_str_info_s auth;
|
28
26
|
/** A `ping` will be sent every `ping_interval` interval or inactivity. */
|
29
27
|
uint8_t ping_interval;
|
30
28
|
};
|
31
29
|
|
32
30
|
/**
|
33
|
-
* See the {
|
31
|
+
* See the {fio.h} file for documentation about engines.
|
34
32
|
*
|
35
33
|
* The engine is active only after facil.io starts running.
|
36
34
|
*
|
@@ -44,7 +42,7 @@ struct redis_engine_create_args {
|
|
44
42
|
* callbacks have been called (or facil.io exits)... If the engine is destroyed
|
45
43
|
* midway, memory leaks might occur (and little puppies might cry).
|
46
44
|
*/
|
47
|
-
|
45
|
+
fio_pubsub_engine_s *redis_engine_create(struct redis_engine_create_args);
|
48
46
|
#define redis_engine_create(...) \
|
49
47
|
redis_engine_create((struct redis_engine_create_args){__VA_ARGS__})
|
50
48
|
|
@@ -61,8 +59,8 @@ pubsub_engine_s *redis_engine_create(struct redis_engine_create_args);
|
|
61
59
|
* Redis connection's protocol (best case scenario, a disconnection will occur
|
62
60
|
* before and messages are lost).
|
63
61
|
*/
|
64
|
-
intptr_t redis_engine_send(
|
65
|
-
void (*callback)(
|
62
|
+
intptr_t redis_engine_send(fio_pubsub_engine_s *engine, FIOBJ command,
|
63
|
+
void (*callback)(fio_pubsub_engine_s *e, FIOBJ reply,
|
66
64
|
void *udata),
|
67
65
|
void *udata);
|
68
66
|
|
@@ -71,7 +69,7 @@ intptr_t redis_engine_send(pubsub_engine_s *engine, FIOBJ command, FIOBJ data,
|
|
71
69
|
*
|
72
70
|
* function names speak for themselves ;-)
|
73
71
|
*/
|
74
|
-
void redis_engine_destroy(
|
72
|
+
void redis_engine_destroy(fio_pubsub_engine_s *engine);
|
75
73
|
|
76
74
|
/* support C++ */
|
77
75
|
#ifdef __cplusplus
|
data/ext/iodine/resp_parser.h
CHANGED
@@ -19,6 +19,10 @@ Feel free to copy, use and enjoy according to the license provided.
|
|
19
19
|
*/
|
20
20
|
#define H_RESP_PARSER_H
|
21
21
|
|
22
|
+
#ifndef _GNU_SOURCE
|
23
|
+
#define _GNU_SOURCE
|
24
|
+
#endif
|
25
|
+
|
22
26
|
#include <stdint.h>
|
23
27
|
#include <stdlib.h>
|
24
28
|
#include <string.h>
|
@@ -177,6 +181,8 @@ Parsing RESP requests
|
|
177
181
|
*/
|
178
182
|
static size_t resp_parse(resp_parser_s *parser, const void *buffer,
|
179
183
|
size_t length) {
|
184
|
+
if (!parser->obj_countdown)
|
185
|
+
parser->obj_countdown = 1; /* always expect something... */
|
180
186
|
uint8_t *pos = (uint8_t *)buffer;
|
181
187
|
const uint8_t *stop = pos + length;
|
182
188
|
while (pos < stop) {
|
@@ -201,7 +207,7 @@ static size_t resp_parse(resp_parser_s *parser, const void *buffer,
|
|
201
207
|
parser->expecting = 0;
|
202
208
|
--parser->obj_countdown;
|
203
209
|
if (parser->obj_countdown <= 0) {
|
204
|
-
parser->obj_countdown =
|
210
|
+
parser->obj_countdown = 1;
|
205
211
|
if (resp_on_message(parser))
|
206
212
|
goto finish;
|
207
213
|
}
|
@@ -224,13 +230,13 @@ static size_t resp_parse(resp_parser_s *parser, const void *buffer,
|
|
224
230
|
goto finish;
|
225
231
|
}
|
226
232
|
resp_on_string_chunk(parser, (void *)(pos + 1),
|
227
|
-
(size_t)((uintptr_t)eol - (uintptr_t)pos -
|
233
|
+
(size_t)((uintptr_t)eol - (uintptr_t)pos - 1));
|
228
234
|
resp_on_end_string(parser);
|
229
235
|
--parser->obj_countdown;
|
230
236
|
break;
|
231
237
|
case '-':
|
232
238
|
resp_on_err_msg(parser, pos,
|
233
|
-
(size_t)((uintptr_t)eol - (uintptr_t)pos -
|
239
|
+
(size_t)((uintptr_t)eol - (uintptr_t)pos - 1));
|
234
240
|
--parser->obj_countdown;
|
235
241
|
break;
|
236
242
|
case '*': /* fallthrough */
|
@@ -276,7 +282,6 @@ static size_t resp_parse(resp_parser_s *parser, const void *buffer,
|
|
276
282
|
case '*':
|
277
283
|
if (i < 0) {
|
278
284
|
resp_on_null(parser);
|
279
|
-
--parser->obj_countdown;
|
280
285
|
} else {
|
281
286
|
if (resp_on_start_array(parser, i)) {
|
282
287
|
pos = eol + 1;
|
@@ -284,6 +289,7 @@ static size_t resp_parse(resp_parser_s *parser, const void *buffer,
|
|
284
289
|
}
|
285
290
|
parser->obj_countdown += i;
|
286
291
|
}
|
292
|
+
--parser->obj_countdown;
|
287
293
|
break;
|
288
294
|
}
|
289
295
|
} break;
|
@@ -300,7 +306,7 @@ static size_t resp_parse(resp_parser_s *parser, const void *buffer,
|
|
300
306
|
}
|
301
307
|
pos = eol + 1;
|
302
308
|
if (parser->obj_countdown <= 0 && !parser->expecting) {
|
303
|
-
parser->obj_countdown =
|
309
|
+
parser->obj_countdown = 1;
|
304
310
|
resp_on_message(parser);
|
305
311
|
}
|
306
312
|
}
|
@@ -7,7 +7,7 @@ Feel free to copy, use and enjoy according to the license specified.
|
|
7
7
|
#ifndef H_WEBSOCKET_PARSER_H
|
8
8
|
/**\file
|
9
9
|
|
10
|
-
A single file
|
10
|
+
A single file WebSocket message parser and WebSocket message wrapper, decoupled
|
11
11
|
from any IO layer.
|
12
12
|
|
13
13
|
Notice that this header file library includes static funnction declerations that
|
@@ -30,7 +30,7 @@ static inline __attribute__((unused)) uint64_t
|
|
30
30
|
websocket_wrapped_len(uint64_t len);
|
31
31
|
|
32
32
|
/**
|
33
|
-
* Wraps a
|
33
|
+
* Wraps a WebSocket server message and writes it to the target buffer.
|
34
34
|
*
|
35
35
|
* The `first` and `last` flags can be used to support message fragmentation.
|
36
36
|
*
|
@@ -38,7 +38,7 @@ websocket_wrapped_len(uint64_t len);
|
|
38
38
|
* * msg: the message to be wrapped.
|
39
39
|
* * len: the message length.
|
40
40
|
* * opcode: set to 1 for UTF-8 message, 2 for binary, etc'.
|
41
|
-
* * first: set to 1 if `msg` points the
|
41
|
+
* * first: set to 1 if `msg` points the beginning of the message.
|
42
42
|
* * last: set to 1 if `msg + len` ends the message.
|
43
43
|
* * client: set to 1 to use client mode (data masking).
|
44
44
|
*
|
@@ -60,7 +60,7 @@ websocket_server_wrap(void *target, void *msg, uint64_t len,
|
|
60
60
|
unsigned char last, unsigned char rsv);
|
61
61
|
|
62
62
|
/**
|
63
|
-
* Wraps a
|
63
|
+
* Wraps a WebSocket client message and writes it to the target buffer.
|
64
64
|
*
|
65
65
|
* The `first` and `last` flags can be used to support message fragmentation.
|
66
66
|
*
|
@@ -68,7 +68,7 @@ websocket_server_wrap(void *target, void *msg, uint64_t len,
|
|
68
68
|
* * msg: the message to be wrapped.
|
69
69
|
* * len: the message length.
|
70
70
|
* * opcode: set to 1 for UTF-8 message, 2 for binary, etc'.
|
71
|
-
* * first: set to 1 if `msg` points the
|
71
|
+
* * first: set to 1 if `msg` points the beginning of the message.
|
72
72
|
* * last: set to 1 if `msg + len` ends the message.
|
73
73
|
* * client: set to 1 to use client mode (data masking).
|
74
74
|
*
|
@@ -121,7 +121,7 @@ websocket_buffer_peek(void *buffer, uint64_t len);
|
|
121
121
|
* Returns the remaining data in the existing buffer (can be 0).
|
122
122
|
*
|
123
123
|
* Notice: if there's any data in the buffer that can't be parsed
|
124
|
-
* just yet, `memmove` is used to place the data at the
|
124
|
+
* just yet, `memmove` is used to place the data at the beginning of the buffer.
|
125
125
|
*/
|
126
126
|
inline static __attribute__((unused)) uint64_t
|
127
127
|
websocket_consume(void *buffer, uint64_t len, void *udata,
|
@@ -241,7 +241,7 @@ Message wrapping
|
|
241
241
|
/** stub byte swap 64 bit integer from pointer */
|
242
242
|
static inline uint64_t netpiswap64(uint8_t *i) {
|
243
243
|
uint64_t ret = 0;
|
244
|
-
uint8_t *const pret = &ret;
|
244
|
+
uint8_t *const pret = (uint8_t *)&ret;
|
245
245
|
pret[7] = i[7];
|
246
246
|
pret[6] = i[6];
|
247
247
|
pret[5] = i[5];
|
@@ -308,7 +308,7 @@ static inline uint64_t websocket_wrapped_len(uint64_t len) {
|
|
308
308
|
}
|
309
309
|
|
310
310
|
/**
|
311
|
-
* Wraps a
|
311
|
+
* Wraps a WebSocket server message and writes it to the target buffer.
|
312
312
|
*
|
313
313
|
* The `first` and `last` flags can be used to support message fragmentation.
|
314
314
|
*
|
@@ -316,7 +316,7 @@ static inline uint64_t websocket_wrapped_len(uint64_t len) {
|
|
316
316
|
* * msg: the message to be wrapped.
|
317
317
|
* * len: the message length.
|
318
318
|
* * opcode: set to 1 for UTF-8 message, 2 for binary, etc'.
|
319
|
-
* * first: set to 1 if `msg` points the
|
319
|
+
* * first: set to 1 if `msg` points the beginning of the message.
|
320
320
|
* * last: set to 1 if `msg + len` ends the message.
|
321
321
|
* * client: set to 1 to use client mode (data masking).
|
322
322
|
*
|
@@ -368,7 +368,7 @@ static uint64_t websocket_server_wrap(void *target, void *msg, uint64_t len,
|
|
368
368
|
}
|
369
369
|
|
370
370
|
/**
|
371
|
-
* Wraps a
|
371
|
+
* Wraps a WebSocket client message and writes it to the target buffer.
|
372
372
|
*
|
373
373
|
* The `first` and `last` flags can be used to support message fragmentation.
|
374
374
|
*
|
@@ -376,7 +376,7 @@ static uint64_t websocket_server_wrap(void *target, void *msg, uint64_t len,
|
|
376
376
|
* * msg: the message to be wrapped.
|
377
377
|
* * len: the message length.
|
378
378
|
* * opcode: set to 1 for UTF-8 message, 2 for binary, etc'.
|
379
|
-
* * first: set to 1 if `msg` points the
|
379
|
+
* * first: set to 1 if `msg` points the beginning of the message.
|
380
380
|
* * last: set to 1 if `msg + len` ends the message.
|
381
381
|
*
|
382
382
|
* Returns the number of bytes written. Always `websocket_wrapped_len(len) +
|
@@ -501,7 +501,7 @@ static uint64_t websocket_consume(void *buffer, uint64_t len, void *udata,
|
|
501
501
|
websocket_xmask(payload, info.packet_length, mask);
|
502
502
|
} else if (require_masking && info.packet_length) {
|
503
503
|
#if DEBUG
|
504
|
-
fprintf(stderr, "ERROR:
|
504
|
+
fprintf(stderr, "ERROR: WebSocket protocol error - unmasked data.\n");
|
505
505
|
#endif
|
506
506
|
websocket_on_protocol_error(udata);
|
507
507
|
}
|
@@ -536,7 +536,7 @@ static uint64_t websocket_consume(void *buffer, uint64_t len, void *udata,
|
|
536
536
|
break;
|
537
537
|
default:
|
538
538
|
#if DEBUG
|
539
|
-
fprintf(stderr, "ERROR:
|
539
|
+
fprintf(stderr, "ERROR: WebSocket protocol error - unknown opcode %u\n",
|
540
540
|
(unsigned int)(pos[0] & 15));
|
541
541
|
#endif
|
542
542
|
websocket_on_protocol_error(udata);
|
data/ext/iodine/websockets.c
CHANGED
@@ -4,18 +4,15 @@ license: MIT
|
|
4
4
|
|
5
5
|
Feel free to copy, use and enjoy according to the license provided.
|
6
6
|
*/
|
7
|
-
#
|
7
|
+
#define FIO_INCLUDE_LINKED_LIST
|
8
|
+
#define FIO_INCLUDE_STR
|
9
|
+
#include <fio.h>
|
8
10
|
|
9
|
-
#include
|
10
|
-
#include "fiobj.h"
|
11
|
+
#include <fiobj.h>
|
11
12
|
|
12
|
-
#include
|
13
|
-
#include
|
14
|
-
#include "fio_sha1.h"
|
15
|
-
#include "http.h"
|
16
|
-
#include "http_internal.h"
|
13
|
+
#include <http.h>
|
14
|
+
#include <http_internal.h>
|
17
15
|
|
18
|
-
#include "pubsub.h"
|
19
16
|
#include <arpa/inet.h>
|
20
17
|
#include <errno.h>
|
21
18
|
#include <stdio.h>
|
@@ -23,9 +20,7 @@ Feel free to copy, use and enjoy according to the license provided.
|
|
23
20
|
#include <string.h>
|
24
21
|
#include <strings.h>
|
25
22
|
|
26
|
-
#include
|
27
|
-
|
28
|
-
#include "websocket_parser.h"
|
23
|
+
#include <websocket_parser.h>
|
29
24
|
|
30
25
|
#if !defined(__BIG_ENDIAN__) && !defined(__LITTLE_ENDIAN__)
|
31
26
|
#include <endian.h>
|
@@ -106,11 +101,11 @@ The Websocket object (protocol + parser)
|
|
106
101
|
*/
|
107
102
|
struct ws_s {
|
108
103
|
/** The Websocket protocol */
|
109
|
-
|
104
|
+
fio_protocol_s protocol;
|
110
105
|
/** connection data */
|
111
106
|
intptr_t fd;
|
112
107
|
/** callbacks */
|
113
|
-
void (*on_message)(ws_s *ws,
|
108
|
+
void (*on_message)(ws_s *ws, fio_str_info_s msg, uint8_t is_text);
|
114
109
|
void (*on_shutdown)(ws_s *ws);
|
115
110
|
void (*on_ready)(ws_s *ws);
|
116
111
|
void (*on_open)(ws_s *ws);
|
@@ -133,18 +128,13 @@ struct ws_s {
|
|
133
128
|
uint8_t is_client;
|
134
129
|
};
|
135
130
|
|
136
|
-
/**
|
137
|
-
The Websocket Protocol Identifying String. Used for the `each` function.
|
138
|
-
*/
|
139
|
-
char *WEBSOCKET_ID_STR = "websockets";
|
140
|
-
|
141
131
|
/* *****************************************************************************
|
142
132
|
Create/Destroy the websocket subscription objects
|
143
133
|
***************************************************************************** */
|
144
134
|
|
145
135
|
static inline void clear_subscriptions(ws_s *ws) {
|
146
136
|
while (fio_ls_any(&ws->subscriptions)) {
|
147
|
-
|
137
|
+
fio_unsubscribe(fio_ls_pop(&ws->subscriptions));
|
148
138
|
}
|
149
139
|
}
|
150
140
|
|
@@ -157,7 +147,8 @@ static void websocket_on_unwrapped(void *ws_p, void *msg, uint64_t len,
|
|
157
147
|
unsigned char rsv) {
|
158
148
|
ws_s *ws = ws_p;
|
159
149
|
if (last && first) {
|
160
|
-
ws->on_message(ws, msg, len,
|
150
|
+
ws->on_message(ws, (fio_str_info_s){.data = msg, .len = len},
|
151
|
+
(uint8_t)text);
|
161
152
|
return;
|
162
153
|
}
|
163
154
|
if (first) {
|
@@ -168,8 +159,7 @@ static void websocket_on_unwrapped(void *ws_p, void *msg, uint64_t len,
|
|
168
159
|
}
|
169
160
|
fiobj_str_write(ws->msg, msg, len);
|
170
161
|
if (last) {
|
171
|
-
|
172
|
-
ws->on_message(ws, (char *)s.data, s.len, ws->is_text);
|
162
|
+
ws->on_message(ws, fiobj_obj2cstr(ws->msg), ws->is_text);
|
173
163
|
}
|
174
164
|
|
175
165
|
(void)rsv;
|
@@ -181,14 +171,14 @@ static void websocket_on_protocol_ping(void *ws_p, void *msg_, uint64_t len) {
|
|
181
171
|
len = (((ws_s *)ws)->is_client
|
182
172
|
? websocket_client_wrap(buff, msg_, len, 10, 1, 1, 0)
|
183
173
|
: websocket_server_wrap(buff, msg_, len, 10, 1, 1, 0));
|
184
|
-
|
174
|
+
fio_write2(ws->fd, .data.buffer = buff, .length = len);
|
185
175
|
} else {
|
186
176
|
if (((ws_s *)ws)->is_client) {
|
187
|
-
|
188
|
-
|
177
|
+
fio_write2(ws->fd, .data.buffer = "\x89\x80mask", .length = 2,
|
178
|
+
.after.dealloc = FIO_DEALLOC_NOOP);
|
189
179
|
} else {
|
190
|
-
|
191
|
-
|
180
|
+
fio_write2(ws->fd, .data.buffer = "\x89\x00", .length = 2,
|
181
|
+
.after.dealloc = FIO_DEALLOC_NOOP);
|
192
182
|
}
|
193
183
|
}
|
194
184
|
}
|
@@ -199,11 +189,11 @@ static void websocket_on_protocol_pong(void *ws_p, void *msg, uint64_t len) {
|
|
199
189
|
}
|
200
190
|
static void websocket_on_protocol_close(void *ws_p) {
|
201
191
|
ws_s *ws = ws_p;
|
202
|
-
|
192
|
+
fio_close(ws->fd);
|
203
193
|
}
|
204
194
|
static void websocket_on_protocol_error(void *ws_p) {
|
205
195
|
ws_s *ws = ws_p;
|
206
|
-
|
196
|
+
fio_close(ws->fd);
|
207
197
|
}
|
208
198
|
|
209
199
|
/*******************************************************************************
|
@@ -212,44 +202,45 @@ The Websocket Protocol implementation
|
|
212
202
|
|
213
203
|
#define ws_protocol(fd) ((ws_s *)(server_get_protocol(fd)))
|
214
204
|
|
215
|
-
static void ws_ping(intptr_t fd,
|
205
|
+
static void ws_ping(intptr_t fd, fio_protocol_s *ws) {
|
216
206
|
(void)(ws);
|
217
207
|
if (((ws_s *)ws)->is_client) {
|
218
|
-
|
219
|
-
|
208
|
+
fio_write2(fd, .data.buffer = "\x89\x80MASK", .length = 6,
|
209
|
+
.after.dealloc = FIO_DEALLOC_NOOP);
|
220
210
|
} else {
|
221
|
-
|
222
|
-
|
211
|
+
fio_write2(fd, .data.buffer = "\x89\x00", .length = 2,
|
212
|
+
.after.dealloc = FIO_DEALLOC_NOOP);
|
223
213
|
}
|
224
214
|
}
|
225
215
|
|
226
|
-
static void on_close(intptr_t uuid,
|
216
|
+
static void on_close(intptr_t uuid, fio_protocol_s *_ws) {
|
227
217
|
destroy_ws((ws_s *)_ws);
|
228
218
|
(void)uuid;
|
229
219
|
}
|
230
220
|
|
231
|
-
static void on_ready(intptr_t fduuid,
|
221
|
+
static void on_ready(intptr_t fduuid, fio_protocol_s *ws) {
|
232
222
|
(void)(fduuid);
|
233
|
-
if (
|
223
|
+
if (((ws_s *)ws)->on_ready)
|
234
224
|
((ws_s *)ws)->on_ready((ws_s *)ws);
|
235
225
|
}
|
236
226
|
|
237
|
-
static
|
227
|
+
static uint8_t on_shutdown(intptr_t fd, fio_protocol_s *ws) {
|
238
228
|
(void)(fd);
|
239
229
|
if (ws && ((ws_s *)ws)->on_shutdown)
|
240
230
|
((ws_s *)ws)->on_shutdown((ws_s *)ws);
|
241
231
|
if (((ws_s *)ws)->is_client) {
|
242
|
-
|
243
|
-
|
232
|
+
fio_write2(fd, .data.buffer = "\x8a\x80MASK", .length = 6,
|
233
|
+
.after.dealloc = FIO_DEALLOC_NOOP);
|
244
234
|
} else {
|
245
|
-
|
246
|
-
|
235
|
+
fio_write2(fd, .data.buffer = "\x8a\x00", .length = 2,
|
236
|
+
.after.dealloc = FIO_DEALLOC_NOOP);
|
247
237
|
}
|
238
|
+
return 0;
|
248
239
|
}
|
249
240
|
|
250
|
-
static void on_data(intptr_t sockfd,
|
241
|
+
static void on_data(intptr_t sockfd, fio_protocol_s *ws_) {
|
251
242
|
ws_s *const ws = (ws_s *)ws_;
|
252
|
-
if (ws == NULL
|
243
|
+
if (ws == NULL)
|
253
244
|
return;
|
254
245
|
struct websocket_packet_info_s info =
|
255
246
|
websocket_buffer_peek(ws->buffer.data, ws->length);
|
@@ -271,18 +262,18 @@ static void on_data(intptr_t sockfd, protocol_s *ws_) {
|
|
271
262
|
}
|
272
263
|
}
|
273
264
|
|
274
|
-
const ssize_t len =
|
275
|
-
|
265
|
+
const ssize_t len = fio_read(sockfd, (uint8_t *)ws->buffer.data + ws->length,
|
266
|
+
ws->buffer.size - ws->length);
|
276
267
|
if (len <= 0) {
|
277
268
|
return;
|
278
269
|
}
|
279
270
|
ws->length = websocket_consume(ws->buffer.data, ws->length + len, ws,
|
280
271
|
(~(ws->is_client) & 1));
|
281
272
|
|
282
|
-
|
273
|
+
fio_force_event(sockfd, FIO_EVENT_ON_DATA);
|
283
274
|
}
|
284
275
|
|
285
|
-
static void on_data_first(intptr_t sockfd,
|
276
|
+
static void on_data_first(intptr_t sockfd, fio_protocol_s *ws_) {
|
286
277
|
ws_s *const ws = (ws_s *)ws_;
|
287
278
|
if (ws->on_open)
|
288
279
|
ws->on_open(ws);
|
@@ -293,9 +284,8 @@ static void on_data_first(intptr_t sockfd, protocol_s *ws_) {
|
|
293
284
|
ws->length = websocket_consume(ws->buffer.data, ws->length, ws,
|
294
285
|
(~(ws->is_client) & 1));
|
295
286
|
}
|
296
|
-
|
297
|
-
|
298
|
-
facil_force_event(sockfd, FIO_EVENT_ON_DATA);
|
287
|
+
fio_force_event(sockfd, FIO_EVENT_ON_DATA);
|
288
|
+
fio_force_event(sockfd, FIO_EVENT_ON_READY);
|
299
289
|
}
|
300
290
|
|
301
291
|
/* later */
|
@@ -310,7 +300,6 @@ static ws_s *new_websocket(intptr_t uuid) {
|
|
310
300
|
// allocate the protocol object
|
311
301
|
ws_s *ws = malloc(sizeof(*ws));
|
312
302
|
*ws = (ws_s){
|
313
|
-
.protocol.service = WEBSOCKET_ID_STR,
|
314
303
|
.protocol.ping = ws_ping,
|
315
304
|
.protocol.on_data = on_data_first,
|
316
305
|
.protocol.on_close = on_close,
|
@@ -355,10 +344,10 @@ void websocket_attach(intptr_t uuid, http_settings_s *http_settings,
|
|
355
344
|
// buffer limits
|
356
345
|
ws->max_msg_size = http_settings->ws_max_msg_size;
|
357
346
|
// update the timeout
|
358
|
-
|
347
|
+
fio_timeout_set(uuid, http_settings->ws_timeout);
|
359
348
|
} else {
|
360
349
|
ws->max_msg_size = (1024 * 256);
|
361
|
-
|
350
|
+
fio_timeout_set(uuid, 40);
|
362
351
|
}
|
363
352
|
|
364
353
|
if (data && length) {
|
@@ -367,7 +356,7 @@ void websocket_attach(intptr_t uuid, http_settings_s *http_settings,
|
|
367
356
|
ws->buffer = resize_ws_buffer(ws, ws->buffer);
|
368
357
|
if (!ws->buffer.data) {
|
369
358
|
// no memory.
|
370
|
-
|
359
|
+
fio_attach(uuid, (fio_protocol_s *)ws);
|
371
360
|
websocket_close(ws);
|
372
361
|
return;
|
373
362
|
}
|
@@ -375,10 +364,10 @@ void websocket_attach(intptr_t uuid, http_settings_s *http_settings,
|
|
375
364
|
memcpy(ws->buffer.data, data, length);
|
376
365
|
ws->length = length;
|
377
366
|
}
|
378
|
-
// update the protocol object,
|
379
|
-
|
367
|
+
// update the protocol object, cleaning up the old one
|
368
|
+
fio_attach(uuid, (fio_protocol_s *)ws);
|
380
369
|
// allow the on_open and on_data to take over the control.
|
381
|
-
|
370
|
+
fio_force_event(uuid, FIO_EVENT_ON_DATA);
|
382
371
|
}
|
383
372
|
|
384
373
|
/*******************************************************************************
|
@@ -387,36 +376,6 @@ Writing to the Websocket
|
|
387
376
|
#define WS_MAX_FRAME_SIZE \
|
388
377
|
(FIO_MEMORY_BLOCK_ALLOC_LIMIT - 4096) // should be less then `unsigned short`
|
389
378
|
|
390
|
-
// clang-format off
|
391
|
-
#if !defined(__BIG_ENDIAN__) && !defined(__LITTLE_ENDIAN__)
|
392
|
-
# if defined(__has_include)
|
393
|
-
# if __has_include(<endian.h>)
|
394
|
-
# include <endian.h>
|
395
|
-
# elif __has_include(<sys/endian.h>)
|
396
|
-
# include <sys/endian.h>
|
397
|
-
# endif
|
398
|
-
# endif
|
399
|
-
# if !defined(__BIG_ENDIAN__) && !defined(__LITTLE_ENDIAN__) && \
|
400
|
-
__BYTE_ORDER__ == __ORDER_BIG_ENDIAN__
|
401
|
-
# define __BIG_ENDIAN__
|
402
|
-
# endif
|
403
|
-
#endif
|
404
|
-
// clang-format on
|
405
|
-
|
406
|
-
#ifdef __BIG_ENDIAN__
|
407
|
-
/** byte swap 64 bit integer */
|
408
|
-
#define bswap64(i) (i)
|
409
|
-
|
410
|
-
#else
|
411
|
-
// TODO: check for __builtin_bswap64
|
412
|
-
/** byte swap 64 bit integer */
|
413
|
-
#define bswap64(i) \
|
414
|
-
((((i)&0xFFULL) << 56) | (((i)&0xFF00ULL) << 40) | \
|
415
|
-
(((i)&0xFF0000ULL) << 24) | (((i)&0xFF000000ULL) << 8) | \
|
416
|
-
(((i)&0xFF00000000ULL) >> 8) | (((i)&0xFF0000000000ULL) >> 24) | \
|
417
|
-
(((i)&0xFF000000000000ULL) >> 40) | (((i)&0xFF00000000000000ULL) >> 56))
|
418
|
-
#endif
|
419
|
-
|
420
379
|
static void websocket_write_impl(intptr_t fd, void *data, size_t len, char text,
|
421
380
|
char first, char last, char client) {
|
422
381
|
if (len <= WS_MAX_FRAME_SIZE) {
|
@@ -425,7 +384,8 @@ static void websocket_write_impl(intptr_t fd, void *data, size_t len, char text,
|
|
425
384
|
first, last, 0)
|
426
385
|
: websocket_server_wrap(buff, data, len, (text ? 1 : 2),
|
427
386
|
first, last, 0));
|
428
|
-
|
387
|
+
fio_write2(fd, .data.buffer = buff, .length = len,
|
388
|
+
.after.dealloc = fio_free);
|
429
389
|
} else {
|
430
390
|
/* frame fragmentation is better for large data then large frames */
|
431
391
|
while (len > WS_MAX_FRAME_SIZE) {
|
@@ -440,77 +400,128 @@ static void websocket_write_impl(intptr_t fd, void *data, size_t len, char text,
|
|
440
400
|
}
|
441
401
|
|
442
402
|
/* *****************************************************************************
|
443
|
-
|
444
|
-
https://stackoverflow.com/a/22135005/4025095
|
445
|
-
and
|
446
|
-
http://bjoern.hoehrmann.de/utf-8/decoder/dfa
|
403
|
+
Multi-client broadcast optimizations
|
447
404
|
***************************************************************************** */
|
448
|
-
/* Copyright (c) 2008-2009 Bjoern Hoehrmann <bjoern@hoehrmann.de> */
|
449
|
-
/* See http://bjoern.hoehrmann.de/utf-8/decoder/dfa/ for details. */
|
450
|
-
|
451
|
-
#define UTF8_ACCEPT 0
|
452
|
-
#define UTF8_REJECT 1
|
453
|
-
|
454
|
-
static const uint8_t utf8d[] = {
|
455
|
-
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
|
456
|
-
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
|
457
|
-
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // 00..1f
|
458
|
-
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
|
459
|
-
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
|
460
|
-
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // 20..3f
|
461
|
-
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
|
462
|
-
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
|
463
|
-
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // 40..5f
|
464
|
-
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
|
465
|
-
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
|
466
|
-
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // 60..7f
|
467
|
-
1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
|
468
|
-
1, 1, 1, 1, 1, 9, 9, 9, 9, 9, 9,
|
469
|
-
9, 9, 9, 9, 9, 9, 9, 9, 9, 9, // 80..9f
|
470
|
-
7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7,
|
471
|
-
7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7,
|
472
|
-
7, 7, 7, 7, 7, 7, 7, 7, 7, 7, // a0..bf
|
473
|
-
8, 8, 2, 2, 2, 2, 2, 2, 2, 2, 2,
|
474
|
-
2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2,
|
475
|
-
2, 2, 2, 2, 2, 2, 2, 2, 2, 2, // c0..df
|
476
|
-
0xa, 0x3, 0x3, 0x3, 0x3, 0x3, 0x3, 0x3, 0x3, 0x3, 0x3,
|
477
|
-
0x3, 0x3, 0x4, 0x3, 0x3, // e0..ef
|
478
|
-
0xb, 0x6, 0x6, 0x6, 0x5, 0x8, 0x8, 0x8, 0x8, 0x8, 0x8,
|
479
|
-
0x8, 0x8, 0x8, 0x8, 0x8, // f0..ff
|
480
|
-
0x0, 0x1, 0x2, 0x3, 0x5, 0x8, 0x7, 0x1, 0x1, 0x1, 0x4,
|
481
|
-
0x6, 0x1, 0x1, 0x1, 0x1, // s0..s0
|
482
|
-
1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
|
483
|
-
1, 1, 1, 1, 1, 1, 0, 1, 1, 1, 1,
|
484
|
-
1, 0, 1, 0, 1, 1, 1, 1, 1, 1, // s1..s2
|
485
|
-
1, 2, 1, 1, 1, 1, 1, 2, 1, 2, 1,
|
486
|
-
1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
|
487
|
-
1, 2, 1, 1, 1, 1, 1, 1, 1, 1, // s3..s4
|
488
|
-
1, 2, 1, 1, 1, 1, 1, 1, 1, 2, 1,
|
489
|
-
1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
|
490
|
-
1, 3, 1, 3, 1, 1, 1, 1, 1, 1, // s5..s6
|
491
|
-
1, 3, 1, 1, 1, 1, 1, 3, 1, 3, 1,
|
492
|
-
1, 1, 1, 1, 1, 1, 3, 1, 1, 1, 1,
|
493
|
-
1, 1, 1, 1, 1, 1, 1, 1, 1, 1, // s7..s8
|
494
|
-
};
|
495
405
|
|
496
|
-
static
|
497
|
-
|
498
|
-
|
499
|
-
|
500
|
-
|
501
|
-
|
502
|
-
|
503
|
-
|
504
|
-
|
406
|
+
static void websocket_optimize_free(fio_msg_s *msg, void *metadata) {
|
407
|
+
fiobj_free((FIOBJ)metadata);
|
408
|
+
(void)msg;
|
409
|
+
}
|
410
|
+
|
411
|
+
static inline fio_msg_metadata_s websocket_optimize(fio_str_info_s msg,
|
412
|
+
unsigned char opcode) {
|
413
|
+
FIOBJ out = fiobj_str_buf(msg.len + 10);
|
414
|
+
fiobj_str_resize(out,
|
415
|
+
websocket_server_wrap(fiobj_obj2cstr(out).data, msg.data,
|
416
|
+
msg.len, opcode, 1, 1, 0));
|
417
|
+
fio_msg_metadata_s ret = {
|
418
|
+
.on_finish = websocket_optimize_free,
|
419
|
+
.metadata = (void *)out,
|
420
|
+
};
|
421
|
+
return ret;
|
422
|
+
}
|
423
|
+
static fio_msg_metadata_s websocket_optimize_generic(fio_str_info_s ch,
|
424
|
+
fio_str_info_s msg,
|
425
|
+
uint8_t is_json) {
|
426
|
+
fio_str_s tmp = FIO_STR_INIT_EXISTING(ch.data, ch.len, 0); // don't free
|
427
|
+
tmp.dealloc = NULL;
|
428
|
+
unsigned char opcode = 2;
|
429
|
+
if (tmp.len <= (2 << 19) && fio_str_utf8_valid(&tmp)) {
|
430
|
+
opcode = 1;
|
505
431
|
}
|
506
|
-
|
432
|
+
fio_msg_metadata_s ret = websocket_optimize(msg, opcode);
|
433
|
+
ret.type_id = WEBSOCKET_OPTIMIZE_PUBSUB;
|
434
|
+
return ret;
|
435
|
+
(void)ch;
|
436
|
+
(void)is_json;
|
437
|
+
}
|
438
|
+
|
439
|
+
static fio_msg_metadata_s websocket_optimize_text(fio_str_info_s ch,
|
440
|
+
fio_str_info_s msg,
|
441
|
+
uint8_t is_json) {
|
442
|
+
fio_msg_metadata_s ret = websocket_optimize(msg, 1);
|
443
|
+
ret.type_id = WEBSOCKET_OPTIMIZE_PUBSUB_TEXT;
|
444
|
+
return ret;
|
445
|
+
(void)ch;
|
446
|
+
(void)is_json;
|
447
|
+
}
|
448
|
+
|
449
|
+
static fio_msg_metadata_s websocket_optimize_binary(fio_str_info_s ch,
|
450
|
+
fio_str_info_s msg,
|
451
|
+
uint8_t is_json) {
|
452
|
+
fio_msg_metadata_s ret = websocket_optimize(msg, 2);
|
453
|
+
ret.type_id = WEBSOCKET_OPTIMIZE_PUBSUB_BINARY;
|
454
|
+
return ret;
|
455
|
+
(void)ch;
|
456
|
+
(void)is_json;
|
507
457
|
}
|
458
|
+
|
459
|
+
/**
|
460
|
+
* Enables (or disables) broadcast optimizations.
|
461
|
+
*
|
462
|
+
* When using WebSocket pub/sub system is originally optimized for either
|
463
|
+
* non-direct transmission (messages are handled by callbacks) or direct
|
464
|
+
* transmission to 1-3 clients per channel (on average), meaning that the
|
465
|
+
* majority of the messages are meant for a single recipient (or multiple
|
466
|
+
* callback recipients) and only some are expected to be directly transmitted to
|
467
|
+
* a group.
|
468
|
+
*
|
469
|
+
* However, when most messages are intended for direct transmission to more than
|
470
|
+
* 3 clients (on average), certain optimizations can be made to improve memory
|
471
|
+
* consumption (minimize duplication or WebSocket network data).
|
472
|
+
*
|
473
|
+
* This function allows enablement (or disablement) of these optimizations.
|
474
|
+
* These optimizations include:
|
475
|
+
*
|
476
|
+
* * WEBSOCKET_OPTIMIZE_PUBSUB - optimize all direct transmission messages,
|
477
|
+
* best attempt to detect Text vs. Binary data.
|
478
|
+
* * WEBSOCKET_OPTIMIZE_PUBSUB_TEXT - optimize direct pub/sub text messages.
|
479
|
+
* * WEBSOCKET_OPTIMIZE_PUBSUB_BINARY - optimize direct pub/sub binary messages.
|
480
|
+
*
|
481
|
+
* Note: to disable an optimization it should be disabled the same amount of
|
482
|
+
* times it was enabled - multiple optimization enablements for the same type
|
483
|
+
* are merged, but reference counted (disabled when reference is zero).
|
484
|
+
*/
|
485
|
+
void websocket_optimize4broadcasts(intptr_t type, int enable) {
|
486
|
+
static intptr_t generic = 0;
|
487
|
+
static intptr_t text = 0;
|
488
|
+
static intptr_t binary = 0;
|
489
|
+
fio_msg_metadata_s (*callback)(fio_str_info_s, fio_str_info_s, uint8_t);
|
490
|
+
intptr_t *counter;
|
491
|
+
switch ((0 - type)) {
|
492
|
+
case (0 - WEBSOCKET_OPTIMIZE_PUBSUB):
|
493
|
+
counter = &generic;
|
494
|
+
callback = websocket_optimize_generic;
|
495
|
+
break;
|
496
|
+
case (0 - WEBSOCKET_OPTIMIZE_PUBSUB_TEXT):
|
497
|
+
counter = &text;
|
498
|
+
callback = websocket_optimize_text;
|
499
|
+
break;
|
500
|
+
case (0 - WEBSOCKET_OPTIMIZE_PUBSUB_BINARY):
|
501
|
+
counter = &binary;
|
502
|
+
callback = websocket_optimize_binary;
|
503
|
+
break;
|
504
|
+
default:
|
505
|
+
return;
|
506
|
+
}
|
507
|
+
if (enable) {
|
508
|
+
if (fio_atomic_add(counter, 1) == 1) {
|
509
|
+
fio_message_metadata_callback_set(callback, 1);
|
510
|
+
}
|
511
|
+
} else {
|
512
|
+
if (fio_atomic_sub(counter, 1) == 0) {
|
513
|
+
fio_message_metadata_callback_set(callback, 0);
|
514
|
+
}
|
515
|
+
}
|
516
|
+
}
|
517
|
+
|
508
518
|
/* *****************************************************************************
|
509
519
|
Subscription handling
|
510
520
|
***************************************************************************** */
|
511
521
|
|
512
522
|
typedef struct {
|
513
|
-
void (*on_message)(
|
523
|
+
void (*on_message)(ws_s *ws, fio_str_info_s channel, fio_str_info_s msg,
|
524
|
+
void *udata);
|
514
525
|
void (*on_unsubscribe)(void *udata);
|
515
526
|
void *udata;
|
516
527
|
} websocket_sub_data_s;
|
@@ -518,73 +529,82 @@ typedef struct {
|
|
518
529
|
static void websocket_on_unsubscribe(void *u1, void *u2) {
|
519
530
|
websocket_sub_data_s *d = u2;
|
520
531
|
(void)u1;
|
521
|
-
if (d->on_unsubscribe)
|
532
|
+
if (d->on_unsubscribe) {
|
522
533
|
d->on_unsubscribe(d->udata);
|
534
|
+
}
|
523
535
|
free(d);
|
524
536
|
}
|
525
537
|
|
526
|
-
static inline void
|
527
|
-
|
528
|
-
|
529
|
-
|
530
|
-
facil_protocol_try_lock((intptr_t)msg->udata1, FIO_PR_LOCK_WRITE);
|
538
|
+
static inline void websocket_on_pubsub_message_direct_internal(fio_msg_s *msg,
|
539
|
+
uint8_t txt) {
|
540
|
+
fio_protocol_s *pr =
|
541
|
+
fio_protocol_try_lock((intptr_t)msg->udata1, FIO_PR_LOCK_WRITE);
|
531
542
|
if (!pr) {
|
532
543
|
if (errno == EBADF)
|
533
544
|
return;
|
534
|
-
|
545
|
+
fio_message_defer(msg);
|
535
546
|
return;
|
536
547
|
}
|
537
|
-
FIOBJ message;
|
538
|
-
|
539
|
-
|
540
|
-
|
541
|
-
|
542
|
-
|
543
|
-
|
544
|
-
|
545
|
-
|
546
|
-
|
547
|
-
|
548
|
-
|
549
|
-
|
550
|
-
|
548
|
+
FIOBJ message = FIOBJ_INVALID;
|
549
|
+
FIOBJ pre_wrapped = FIOBJ_INVALID;
|
550
|
+
switch (txt) {
|
551
|
+
case 0:
|
552
|
+
pre_wrapped =
|
553
|
+
(FIOBJ)fio_message_metadata(msg, WEBSOCKET_OPTIMIZE_PUBSUB_BINARY);
|
554
|
+
break;
|
555
|
+
case 1:
|
556
|
+
pre_wrapped =
|
557
|
+
(FIOBJ)fio_message_metadata(msg, WEBSOCKET_OPTIMIZE_PUBSUB_TEXT);
|
558
|
+
break;
|
559
|
+
case 2:
|
560
|
+
pre_wrapped = (FIOBJ)fio_message_metadata(msg, WEBSOCKET_OPTIMIZE_PUBSUB);
|
561
|
+
break;
|
562
|
+
default:
|
563
|
+
break;
|
564
|
+
}
|
565
|
+
if (pre_wrapped) {
|
566
|
+
// fprintf(stderr, "INFO: WebSocket Pub/Sub optimized for broadcast\n");
|
567
|
+
fiobj_send_free((intptr_t)msg->udata1, fiobj_dup(pre_wrapped));
|
568
|
+
goto finish;
|
551
569
|
}
|
552
|
-
|
553
|
-
|
570
|
+
if (txt == 2) {
|
571
|
+
/* unknown text state */
|
572
|
+
fio_str_s tmp =
|
573
|
+
FIO_STR_INIT_EXISTING(msg->msg.data, msg->msg.len, 0); // don't free
|
574
|
+
txt = (tmp.len >= (2 << 14) ? 0 : fio_str_utf8_valid(&tmp));
|
575
|
+
}
|
576
|
+
websocket_write((ws_s *)pr, msg->msg, txt & 1);
|
554
577
|
fiobj_free(message);
|
578
|
+
finish:
|
579
|
+
fio_protocol_unlock(pr, FIO_PR_LOCK_WRITE);
|
555
580
|
}
|
556
581
|
|
557
|
-
static void websocket_on_pubsub_message_direct(
|
582
|
+
static void websocket_on_pubsub_message_direct(fio_msg_s *msg) {
|
558
583
|
websocket_on_pubsub_message_direct_internal(msg, 2);
|
559
584
|
}
|
560
585
|
|
561
|
-
static void websocket_on_pubsub_message_direct_txt(
|
586
|
+
static void websocket_on_pubsub_message_direct_txt(fio_msg_s *msg) {
|
562
587
|
websocket_on_pubsub_message_direct_internal(msg, 1);
|
563
588
|
}
|
564
589
|
|
565
|
-
static void websocket_on_pubsub_message_direct_bin(
|
590
|
+
static void websocket_on_pubsub_message_direct_bin(fio_msg_s *msg) {
|
566
591
|
websocket_on_pubsub_message_direct_internal(msg, 0);
|
567
592
|
}
|
568
593
|
|
569
|
-
static void websocket_on_pubsub_message(
|
570
|
-
|
571
|
-
|
594
|
+
static void websocket_on_pubsub_message(fio_msg_s *msg) {
|
595
|
+
fio_protocol_s *pr =
|
596
|
+
fio_protocol_try_lock((intptr_t)msg->udata1, FIO_PR_LOCK_TASK);
|
572
597
|
if (!pr) {
|
573
598
|
if (errno == EBADF)
|
574
599
|
return;
|
575
|
-
|
600
|
+
fio_message_defer(msg);
|
576
601
|
return;
|
577
602
|
}
|
578
603
|
websocket_sub_data_s *d = msg->udata2;
|
579
604
|
|
580
605
|
if (d->on_message)
|
581
|
-
d->on_message((
|
582
|
-
|
583
|
-
.subscription_id = (intptr_t)msg->subscription,
|
584
|
-
.channel = msg->channel,
|
585
|
-
.message = msg->message,
|
586
|
-
});
|
587
|
-
facil_protocol_unlock(pr, FIO_PR_LOCK_TASK);
|
606
|
+
d->on_message((ws_s *)pr, msg->channel, msg->msg, d->udata);
|
607
|
+
fio_protocol_unlock(pr, FIO_PR_LOCK_TASK);
|
588
608
|
}
|
589
609
|
|
590
610
|
/**
|
@@ -595,12 +615,17 @@ uintptr_t websocket_subscribe(struct websocket_subscribe_s args) {
|
|
595
615
|
if (!args.ws)
|
596
616
|
goto error;
|
597
617
|
websocket_sub_data_s *d = malloc(sizeof(*d));
|
598
|
-
|
599
|
-
|
600
|
-
|
601
|
-
|
602
|
-
|
603
|
-
|
618
|
+
if (!d) {
|
619
|
+
websocket_close(args.ws);
|
620
|
+
goto error;
|
621
|
+
}
|
622
|
+
*d = (websocket_sub_data_s){
|
623
|
+
.udata = args.udata,
|
624
|
+
.on_message = args.on_message,
|
625
|
+
.on_unsubscribe = args.on_unsubscribe,
|
626
|
+
};
|
627
|
+
subscription_s *sub = fio_subscribe(
|
628
|
+
.channel = args.channel, .match = args.match,
|
604
629
|
.on_unsubscribe = websocket_on_unsubscribe,
|
605
630
|
.on_message =
|
606
631
|
(args.on_message
|
@@ -612,7 +637,7 @@ uintptr_t websocket_subscribe(struct websocket_subscribe_s args) {
|
|
612
637
|
: websocket_on_pubsub_message_direct),
|
613
638
|
.udata1 = (void *)args.ws->fd, .udata2 = d);
|
614
639
|
if (!sub) {
|
615
|
-
free(d)
|
640
|
+
/* don't free `d`, return (`d` freed by callback) */
|
616
641
|
return 0;
|
617
642
|
}
|
618
643
|
fio_ls_push(&args.ws->subscriptions, sub);
|
@@ -623,37 +648,11 @@ error:
|
|
623
648
|
return 0;
|
624
649
|
}
|
625
650
|
|
626
|
-
/**
|
627
|
-
* Returns the existing subscription's ID (if exists) or 0 (no subscription).
|
628
|
-
*/
|
629
|
-
#undef websocket_find_sub
|
630
|
-
uintptr_t websocket_find_sub(struct websocket_subscribe_s args) {
|
631
|
-
pubsub_sub_pt sub = pubsub_find_sub(
|
632
|
-
.channel = args.channel, .use_pattern = args.use_pattern,
|
633
|
-
.on_unsubscribe = websocket_on_unsubscribe,
|
634
|
-
.on_message =
|
635
|
-
(args.on_message
|
636
|
-
? websocket_on_pubsub_message
|
637
|
-
: args.force_binary
|
638
|
-
? websocket_on_pubsub_message_direct_bin
|
639
|
-
: args.force_text
|
640
|
-
? websocket_on_pubsub_message_direct_txt
|
641
|
-
: websocket_on_pubsub_message_direct),
|
642
|
-
.udata1 = (void *)args.ws->fd, .udata2 = args.udata);
|
643
|
-
if (!sub)
|
644
|
-
return 0;
|
645
|
-
FIO_LS_FOR(&args.ws->subscriptions, pos) {
|
646
|
-
if (pos->obj == sub)
|
647
|
-
return (uintptr_t)pos;
|
648
|
-
}
|
649
|
-
return 0;
|
650
|
-
}
|
651
|
-
|
652
651
|
/**
|
653
652
|
* Unsubscribes from a channel.
|
654
653
|
*/
|
655
654
|
void websocket_unsubscribe(ws_s *ws, uintptr_t subscription_id) {
|
656
|
-
|
655
|
+
fio_unsubscribe((subscription_s *)((fio_ls_s *)subscription_id)->obj);
|
657
656
|
fio_ls_remove((fio_ls_s *)subscription_id);
|
658
657
|
(void)ws;
|
659
658
|
}
|
@@ -663,9 +662,11 @@ The API implementation
|
|
663
662
|
*/
|
664
663
|
|
665
664
|
/** Returns the opaque user data associated with the websocket. */
|
666
|
-
void *
|
665
|
+
void *websocket_udata_get(ws_s *ws) { return ws->udata; }
|
666
|
+
|
667
667
|
/** Returns the the process specific connection's UUID (see `libsock`). */
|
668
668
|
intptr_t websocket_uuid(ws_s *ws) { return ws->fd; }
|
669
|
+
|
669
670
|
/** Sets the opaque user data associated with the websocket.
|
670
671
|
* Returns the old value, if any. */
|
671
672
|
void *websocket_udata_set(ws_s *ws, void *udata) {
|
@@ -673,181 +674,27 @@ void *websocket_udata_set(ws_s *ws, void *udata) {
|
|
673
674
|
ws->udata = udata;
|
674
675
|
return old;
|
675
676
|
}
|
677
|
+
|
678
|
+
/**
|
679
|
+
* Returns 1 if the WebSocket connection is in Client mode (connected to a
|
680
|
+
* remote server) and 0 if the connection is in Server mode (a connection
|
681
|
+
* established using facil.io's HTTP server).
|
682
|
+
*/
|
683
|
+
uint8_t websocket_is_client(ws_s *ws) { return ws->is_client; }
|
684
|
+
|
676
685
|
/** Writes data to the websocket. Returns -1 on failure (0 on success). */
|
677
|
-
int websocket_write(ws_s *ws,
|
678
|
-
if (
|
679
|
-
websocket_write_impl(ws->fd, data,
|
686
|
+
int websocket_write(ws_s *ws, fio_str_info_s msg, uint8_t is_text) {
|
687
|
+
if (fio_is_valid(ws->fd)) {
|
688
|
+
websocket_write_impl(ws->fd, msg.data, msg.len, is_text, 1, 1,
|
689
|
+
ws->is_client);
|
680
690
|
return 0;
|
681
691
|
}
|
682
692
|
return -1;
|
683
693
|
}
|
684
694
|
/** Closes a websocket connection. */
|
685
695
|
void websocket_close(ws_s *ws) {
|
686
|
-
|
687
|
-
|
688
|
-
|
696
|
+
fio_write2(ws->fd, .data.buffer = "\x88\x00", .length = 2,
|
697
|
+
.after.dealloc = FIO_DEALLOC_NOOP);
|
698
|
+
fio_close(ws->fd);
|
689
699
|
return;
|
690
700
|
}
|
691
|
-
|
692
|
-
/**
|
693
|
-
Counts the number of websocket connections.
|
694
|
-
*/
|
695
|
-
size_t websocket_count(void) { return facil_count(WEBSOCKET_ID_STR); }
|
696
|
-
|
697
|
-
/*******************************************************************************
|
698
|
-
Each Implementation
|
699
|
-
*/
|
700
|
-
|
701
|
-
/** A task container. */
|
702
|
-
struct WSTask {
|
703
|
-
void (*task)(ws_s *, void *);
|
704
|
-
void (*on_finish)(ws_s *, void *);
|
705
|
-
void *arg;
|
706
|
-
};
|
707
|
-
/** Performs a task on each websocket connection that shares the same process
|
708
|
-
*/
|
709
|
-
static void perform_ws_task(intptr_t fd, protocol_s *ws_, void *tsk_) {
|
710
|
-
(void)(fd);
|
711
|
-
struct WSTask *tsk = tsk_;
|
712
|
-
tsk->task((ws_s *)(ws_), tsk->arg);
|
713
|
-
}
|
714
|
-
/** clears away a wesbocket task. */
|
715
|
-
static void finish_ws_task(intptr_t fd, void *arg) {
|
716
|
-
struct WSTask *tsk = arg;
|
717
|
-
if (tsk->on_finish) {
|
718
|
-
protocol_s *ws = facil_protocol_try_lock(fd, FIO_PR_LOCK_TASK);
|
719
|
-
if (!ws && errno != EBADF) {
|
720
|
-
defer((void (*)(void *, void *))finish_ws_task, (void *)fd, arg);
|
721
|
-
return;
|
722
|
-
}
|
723
|
-
tsk->on_finish((ws_s *)ws, tsk->arg);
|
724
|
-
if (ws)
|
725
|
-
facil_protocol_unlock(ws, FIO_PR_LOCK_TASK);
|
726
|
-
}
|
727
|
-
free(tsk);
|
728
|
-
}
|
729
|
-
|
730
|
-
/**
|
731
|
-
Performs a task on each websocket connection that shares the same process
|
732
|
-
(except the originating `ws_s` connection which is allowed to be NULL).
|
733
|
-
*/
|
734
|
-
#undef websocket_each
|
735
|
-
void __attribute__((deprecated))
|
736
|
-
websocket_each(struct websocket_each_args_s args) {
|
737
|
-
struct WSTask *tsk = malloc(sizeof(*tsk));
|
738
|
-
tsk->arg = args.arg;
|
739
|
-
tsk->on_finish = args.on_finish;
|
740
|
-
tsk->task = args.task;
|
741
|
-
facil_each(.origin = (args.origin ? args.origin->fd : -1),
|
742
|
-
.service = WEBSOCKET_ID_STR, .task = perform_ws_task, .arg = tsk,
|
743
|
-
.on_complete = finish_ws_task);
|
744
|
-
}
|
745
|
-
/*******************************************************************************
|
746
|
-
Multi-Write (direct broadcast) Implementation
|
747
|
-
*/
|
748
|
-
struct websocket_multi_write {
|
749
|
-
uint8_t (*if_callback)(ws_s *ws_to, void *arg);
|
750
|
-
void (*on_finished)(ws_s *ws_origin, void *arg);
|
751
|
-
intptr_t origin;
|
752
|
-
void *arg;
|
753
|
-
spn_lock_i lock;
|
754
|
-
/* ... we need to have padding for pointer arithmatics... */
|
755
|
-
uint8_t as_client;
|
756
|
-
/* ... we need to have padding for pointer arithmatics... */
|
757
|
-
size_t count;
|
758
|
-
size_t length;
|
759
|
-
uint8_t buffer[]; /* starts on border alignment */
|
760
|
-
};
|
761
|
-
|
762
|
-
static void ws_mw_defered_on_finish_fb(intptr_t fd, void *arg) {
|
763
|
-
(void)(fd);
|
764
|
-
struct websocket_multi_write *fin = arg;
|
765
|
-
if (fin->on_finished)
|
766
|
-
fin->on_finished(NULL, fin->arg);
|
767
|
-
free(fin);
|
768
|
-
}
|
769
|
-
static void ws_mw_defered_on_finish(intptr_t fd, protocol_s *ws, void *arg) {
|
770
|
-
(void)fd;
|
771
|
-
struct websocket_multi_write *fin = arg;
|
772
|
-
if (fin->on_finished) {
|
773
|
-
fin->on_finished((ws_s *)ws, fin->arg);
|
774
|
-
}
|
775
|
-
free(fin);
|
776
|
-
}
|
777
|
-
|
778
|
-
static void ws_reduce_or_free_multi_write(void *buff) {
|
779
|
-
struct websocket_multi_write *mw = (void *)((uintptr_t)buff - sizeof(*mw));
|
780
|
-
spn_lock(&mw->lock);
|
781
|
-
mw->count -= 1;
|
782
|
-
if (!mw->count) {
|
783
|
-
spn_unlock(&mw->lock);
|
784
|
-
if (mw->on_finished) {
|
785
|
-
facil_defer(.uuid = mw->origin, .task = ws_mw_defered_on_finish,
|
786
|
-
.arg = mw, .fallback = ws_mw_defered_on_finish_fb,
|
787
|
-
.type = FIO_PR_LOCK_WRITE);
|
788
|
-
} else
|
789
|
-
free(mw);
|
790
|
-
} else
|
791
|
-
spn_unlock(&mw->lock);
|
792
|
-
}
|
793
|
-
|
794
|
-
static void ws_finish_multi_write(intptr_t fd, void *arg) {
|
795
|
-
struct websocket_multi_write *multi = arg;
|
796
|
-
(void)(fd);
|
797
|
-
ws_reduce_or_free_multi_write(multi->buffer);
|
798
|
-
}
|
799
|
-
|
800
|
-
static void ws_direct_multi_write(intptr_t fd, protocol_s *_ws, void *arg) {
|
801
|
-
struct websocket_multi_write *multi = arg;
|
802
|
-
if (((ws_s *)(_ws))->is_client != multi->as_client)
|
803
|
-
return;
|
804
|
-
spn_lock(&multi->lock);
|
805
|
-
multi->count += 1;
|
806
|
-
spn_unlock(&multi->lock);
|
807
|
-
sock_write2(.uuid = fd, .buffer = multi->buffer, .length = multi->length,
|
808
|
-
.dealloc = ws_reduce_or_free_multi_write);
|
809
|
-
}
|
810
|
-
|
811
|
-
static void ws_check_multi_write(intptr_t fd, protocol_s *_ws, void *arg) {
|
812
|
-
struct websocket_multi_write *multi = arg;
|
813
|
-
if (((ws_s *)(_ws))->is_client != multi->as_client)
|
814
|
-
return;
|
815
|
-
if (multi->if_callback((void *)_ws, multi->arg))
|
816
|
-
ws_direct_multi_write(fd, _ws, arg);
|
817
|
-
}
|
818
|
-
|
819
|
-
#undef websocket_write_each
|
820
|
-
int __attribute__((deprecated))
|
821
|
-
websocket_write_each(struct websocket_write_each_args_s args) {
|
822
|
-
if (!args.data || !args.length)
|
823
|
-
return -1;
|
824
|
-
struct websocket_multi_write *multi =
|
825
|
-
malloc(sizeof(*multi) + args.length + 16 /* max head size + 2 */);
|
826
|
-
if (!multi) {
|
827
|
-
if (args.on_finished)
|
828
|
-
defer((void (*)(void *, void *))args.on_finished, NULL, args.arg);
|
829
|
-
return -1;
|
830
|
-
}
|
831
|
-
*multi = (struct websocket_multi_write){
|
832
|
-
.length =
|
833
|
-
(args.as_client
|
834
|
-
? websocket_client_wrap(multi->buffer, args.data, args.length,
|
835
|
-
args.is_text ? 1 : 2, 1, 1, 0)
|
836
|
-
: websocket_server_wrap(multi->buffer, args.data, args.length,
|
837
|
-
args.is_text ? 1 : 2, 1, 1, 0)),
|
838
|
-
.if_callback = args.filter,
|
839
|
-
.on_finished = args.on_finished,
|
840
|
-
.arg = args.arg,
|
841
|
-
.origin = (args.origin ? args.origin->fd : -1),
|
842
|
-
.as_client = args.as_client,
|
843
|
-
.lock = SPN_LOCK_INIT,
|
844
|
-
.count = 1,
|
845
|
-
};
|
846
|
-
|
847
|
-
facil_each(.origin = multi->origin, .service = WEBSOCKET_ID_STR,
|
848
|
-
.task_type = FIO_PR_LOCK_WRITE,
|
849
|
-
.task =
|
850
|
-
(args.filter ? ws_check_multi_write : ws_direct_multi_write),
|
851
|
-
.arg = multi, .on_complete = ws_finish_multi_write);
|
852
|
-
return 0;
|
853
|
-
}
|