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.

Files changed (98) hide show
  1. checksums.yaml +4 -4
  2. data/CHANGELOG.md +11 -0
  3. data/README.md +4 -4
  4. data/SPEC-Websocket-Draft.md +3 -6
  5. data/bin/mustache.rb +128 -0
  6. data/examples/test_template.mustache +16 -0
  7. data/ext/iodine/fio.c +9397 -0
  8. data/ext/iodine/fio.h +4723 -0
  9. data/ext/iodine/fio_ary.h +353 -54
  10. data/ext/iodine/fio_cli.c +351 -361
  11. data/ext/iodine/fio_cli.h +84 -105
  12. data/ext/iodine/fio_hashmap.h +70 -16
  13. data/ext/iodine/fio_json_parser.h +35 -24
  14. data/ext/iodine/fio_siphash.c +104 -4
  15. data/ext/iodine/fio_siphash.h +18 -2
  16. data/ext/iodine/fio_str.h +1218 -0
  17. data/ext/iodine/fio_tmpfile.h +1 -1
  18. data/ext/iodine/fiobj.h +13 -8
  19. data/ext/iodine/fiobj4sock.h +6 -8
  20. data/ext/iodine/fiobj_ary.c +107 -17
  21. data/ext/iodine/fiobj_ary.h +36 -4
  22. data/ext/iodine/fiobj_data.c +146 -127
  23. data/ext/iodine/fiobj_data.h +25 -23
  24. data/ext/iodine/fiobj_hash.c +7 -7
  25. data/ext/iodine/fiobj_hash.h +6 -5
  26. data/ext/iodine/fiobj_json.c +20 -17
  27. data/ext/iodine/fiobj_json.h +5 -5
  28. data/ext/iodine/fiobj_mem.h +71 -0
  29. data/ext/iodine/fiobj_mustache.c +310 -0
  30. data/ext/iodine/fiobj_mustache.h +40 -0
  31. data/ext/iodine/fiobj_numbers.c +199 -94
  32. data/ext/iodine/fiobj_numbers.h +7 -7
  33. data/ext/iodine/fiobj_str.c +142 -333
  34. data/ext/iodine/fiobj_str.h +65 -55
  35. data/ext/iodine/fiobject.c +49 -11
  36. data/ext/iodine/fiobject.h +40 -39
  37. data/ext/iodine/http.c +382 -190
  38. data/ext/iodine/http.h +124 -80
  39. data/ext/iodine/http1.c +99 -127
  40. data/ext/iodine/http1.h +5 -5
  41. data/ext/iodine/http1_parser.c +3 -2
  42. data/ext/iodine/http1_parser.h +2 -2
  43. data/ext/iodine/http_internal.c +14 -12
  44. data/ext/iodine/http_internal.h +25 -19
  45. data/ext/iodine/iodine.c +37 -18
  46. data/ext/iodine/iodine.h +4 -0
  47. data/ext/iodine/iodine_caller.c +9 -2
  48. data/ext/iodine/iodine_caller.h +2 -0
  49. data/ext/iodine/iodine_connection.c +82 -117
  50. data/ext/iodine/iodine_defer.c +57 -50
  51. data/ext/iodine/iodine_defer.h +0 -1
  52. data/ext/iodine/iodine_fiobj2rb.h +4 -2
  53. data/ext/iodine/iodine_helpers.c +4 -4
  54. data/ext/iodine/iodine_http.c +25 -32
  55. data/ext/iodine/iodine_json.c +2 -1
  56. data/ext/iodine/iodine_mustache.c +423 -0
  57. data/ext/iodine/iodine_mustache.h +6 -0
  58. data/ext/iodine/iodine_pubsub.c +48 -153
  59. data/ext/iodine/iodine_pubsub.h +5 -4
  60. data/ext/iodine/iodine_rack_io.c +7 -5
  61. data/ext/iodine/iodine_store.c +16 -13
  62. data/ext/iodine/iodine_tcp.c +26 -34
  63. data/ext/iodine/mustache_parser.h +1085 -0
  64. data/ext/iodine/redis_engine.c +740 -646
  65. data/ext/iodine/redis_engine.h +13 -15
  66. data/ext/iodine/resp_parser.h +11 -5
  67. data/ext/iodine/websocket_parser.h +13 -13
  68. data/ext/iodine/websockets.c +240 -393
  69. data/ext/iodine/websockets.h +52 -113
  70. data/lib/iodine.rb +1 -1
  71. data/lib/iodine/mustache.rb +140 -0
  72. data/lib/iodine/version.rb +1 -1
  73. metadata +15 -28
  74. data/ext/iodine/defer.c +0 -566
  75. data/ext/iodine/defer.h +0 -148
  76. data/ext/iodine/evio.c +0 -26
  77. data/ext/iodine/evio.h +0 -161
  78. data/ext/iodine/evio_callbacks.c +0 -26
  79. data/ext/iodine/evio_epoll.c +0 -251
  80. data/ext/iodine/evio_kqueue.c +0 -194
  81. data/ext/iodine/facil.c +0 -2325
  82. data/ext/iodine/facil.h +0 -616
  83. data/ext/iodine/fio_base64.c +0 -277
  84. data/ext/iodine/fio_base64.h +0 -71
  85. data/ext/iodine/fio_llist.h +0 -257
  86. data/ext/iodine/fio_mem.c +0 -675
  87. data/ext/iodine/fio_mem.h +0 -143
  88. data/ext/iodine/fio_random.c +0 -248
  89. data/ext/iodine/fio_random.h +0 -45
  90. data/ext/iodine/fio_sha1.c +0 -362
  91. data/ext/iodine/fio_sha1.h +0 -107
  92. data/ext/iodine/fio_sha2.c +0 -842
  93. data/ext/iodine/fio_sha2.h +0 -169
  94. data/ext/iodine/pubsub.c +0 -867
  95. data/ext/iodine/pubsub.h +0 -221
  96. data/ext/iodine/sock.c +0 -1366
  97. data/ext/iodine/sock.h +0 -566
  98. data/ext/iodine/spnlock.inc +0 -111
@@ -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 "fiobj.h"
11
- #include "pubsub.h"
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
- const char *address;
22
- /** Redis server's port */
23
- const char *port;
24
- /** Redis server's password, if any */
25
- const char *auth;
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 {pubsub.h} file for documentation about engines.
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
- pubsub_engine_s *redis_engine_create(struct redis_engine_create_args);
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(pubsub_engine_s *engine, FIOBJ command, FIOBJ data,
65
- void (*callback)(pubsub_engine_s *e, FIOBJ reply,
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(pubsub_engine_s *engine);
72
+ void redis_engine_destroy(fio_pubsub_engine_s *engine);
75
73
 
76
74
  /* support C++ */
77
75
  #ifdef __cplusplus
@@ -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 = 0;
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 - 2));
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 - 2));
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 = 0;
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 Websocket message parser and Websocket message wrapper, decoupled
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 Websocket server message and writes it to the target buffer.
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 begining of the message.
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 Websocket client message and writes it to the target buffer.
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 begining of the message.
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 begining of the buffer.
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 Websocket server message and writes it to the target buffer.
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 begining of the message.
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 Websocket client message and writes it to the target buffer.
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 begining of the message.
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: Websocket protocol error - unmasked data.\n");
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: Websocket protocol error - unknown opcode %u\n",
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);
@@ -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
- #include "spnlock.inc"
7
+ #define FIO_INCLUDE_LINKED_LIST
8
+ #define FIO_INCLUDE_STR
9
+ #include <fio.h>
8
10
 
9
- #include "fio_llist.h"
10
- #include "fiobj.h"
11
+ #include <fiobj.h>
11
12
 
12
- #include "evio.h"
13
- #include "fio_base64.h"
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 "fio_mem.h"
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
- protocol_s protocol;
104
+ fio_protocol_s protocol;
110
105
  /** connection data */
111
106
  intptr_t fd;
112
107
  /** callbacks */
113
- void (*on_message)(ws_s *ws, char *data, size_t size, uint8_t is_text);
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
- pubsub_unsubscribe(fio_ls_pop(&ws->subscriptions));
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, (uint8_t)text);
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
- fio_cstr_s s = fiobj_obj2cstr(ws->msg);
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
- sock_write2(.uuid = ws->fd, .buffer = buff, .length = len);
174
+ fio_write2(ws->fd, .data.buffer = buff, .length = len);
185
175
  } else {
186
176
  if (((ws_s *)ws)->is_client) {
187
- sock_write2(.uuid = ws->fd, .buffer = "\x89\x80mask", .length = 2,
188
- .dealloc = SOCK_DEALLOC_NOOP);
177
+ fio_write2(ws->fd, .data.buffer = "\x89\x80mask", .length = 2,
178
+ .after.dealloc = FIO_DEALLOC_NOOP);
189
179
  } else {
190
- sock_write2(.uuid = ws->fd, .buffer = "\x89\x00", .length = 2,
191
- .dealloc = SOCK_DEALLOC_NOOP);
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
- sock_close(ws->fd);
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
- sock_close(ws->fd);
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, protocol_s *ws) {
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
- sock_write2(.uuid = fd, .buffer = "\x89\x80MASK", .length = 6,
219
- .dealloc = SOCK_DEALLOC_NOOP);
208
+ fio_write2(fd, .data.buffer = "\x89\x80MASK", .length = 6,
209
+ .after.dealloc = FIO_DEALLOC_NOOP);
220
210
  } else {
221
- sock_write2(.uuid = fd, .buffer = "\x89\x00", .length = 2,
222
- .dealloc = SOCK_DEALLOC_NOOP);
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, protocol_s *_ws) {
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, protocol_s *ws) {
221
+ static void on_ready(intptr_t fduuid, fio_protocol_s *ws) {
232
222
  (void)(fduuid);
233
- if (ws && ws->service == WEBSOCKET_ID_STR && ((ws_s *)ws)->on_ready)
223
+ if (((ws_s *)ws)->on_ready)
234
224
  ((ws_s *)ws)->on_ready((ws_s *)ws);
235
225
  }
236
226
 
237
- static void on_shutdown(intptr_t fd, protocol_s *ws) {
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
- sock_write2(.uuid = fd, .buffer = "\x8a\x80MASK", .length = 6,
243
- .dealloc = SOCK_DEALLOC_NOOP);
232
+ fio_write2(fd, .data.buffer = "\x8a\x80MASK", .length = 6,
233
+ .after.dealloc = FIO_DEALLOC_NOOP);
244
234
  } else {
245
- sock_write2(.uuid = fd, .buffer = "\x8a\x00", .length = 2,
246
- .dealloc = SOCK_DEALLOC_NOOP);
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, protocol_s *ws_) {
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 || ws->protocol.service != WEBSOCKET_ID_STR)
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 = sock_read(sockfd, (uint8_t *)ws->buffer.data + ws->length,
275
- ws->buffer.size - ws->length);
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
- facil_force_event(sockfd, FIO_EVENT_ON_DATA);
273
+ fio_force_event(sockfd, FIO_EVENT_ON_DATA);
283
274
  }
284
275
 
285
- static void on_data_first(intptr_t sockfd, protocol_s *ws_) {
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
- evio_add_write(sock_uuid2fd(sockfd), (void *)sockfd);
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
- facil_set_timeout(uuid, http_settings->ws_timeout);
347
+ fio_timeout_set(uuid, http_settings->ws_timeout);
359
348
  } else {
360
349
  ws->max_msg_size = (1024 * 256);
361
- facil_set_timeout(uuid, 40);
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
- facil_attach(uuid, (protocol_s *)ws);
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, cleanning up the old one
379
- facil_attach(uuid, (protocol_s *)ws);
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
- facil_force_event(uuid, FIO_EVENT_ON_DATA);
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
- sock_write2(.uuid = fd, .buffer = buff, .length = len, .dealloc = fio_free);
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
- UTF-8 testing. This part was practically copied from:
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 inline uint32_t validate_utf8(uint8_t *str, size_t len) {
497
- uint32_t state = 0;
498
- while (len) {
499
- uint32_t type = utf8d[*str];
500
- state = utf8d[256 + state * 16 + type];
501
- if (state == UTF8_REJECT)
502
- return 0;
503
- len--;
504
- str++;
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
- return state == 0;
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)(websocket_pubsub_notification_s notification);
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
- websocket_on_pubsub_message_direct_internal(pubsub_message_s *msg,
528
- uint8_t txt) {
529
- protocol_s *pr =
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
- pubsub_defer(msg);
545
+ fio_message_defer(msg);
535
546
  return;
536
547
  }
537
- FIOBJ message;
538
- fio_cstr_s tmp;
539
- if (FIOBJ_TYPE_IS(msg->message, FIOBJ_T_STRING)) {
540
- message = fiobj_dup(msg->message);
541
- tmp = fiobj_obj2cstr(message);
542
- if (txt == 2) {
543
- /* unknown text state */
544
- txt =
545
- (tmp.len >= (2 << 14) ? 0
546
- : validate_utf8((uint8_t *)tmp.data, tmp.len));
547
- }
548
- } else {
549
- message = fiobj_obj2json(msg->message, 0);
550
- tmp = fiobj_obj2cstr(message);
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
- websocket_write((ws_s *)pr, tmp.data, tmp.len, txt & 1);
553
- facil_protocol_unlock(pr, FIO_PR_LOCK_WRITE);
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(pubsub_message_s *msg) {
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(pubsub_message_s *msg) {
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(pubsub_message_s *msg) {
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(pubsub_message_s *msg) {
570
- protocol_s *pr =
571
- facil_protocol_try_lock((intptr_t)msg->udata1, FIO_PR_LOCK_TASK);
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
- pubsub_defer(msg);
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((websocket_pubsub_notification_s){
582
- .ws = (ws_s *)pr,
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
- *d = (websocket_sub_data_s){.udata = args.udata,
599
- .on_message = args.on_message,
600
- .on_unsubscribe = args.on_unsubscribe};
601
-
602
- pubsub_sub_pt sub = pubsub_subscribe(
603
- .channel = args.channel, .use_pattern = args.use_pattern,
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
- pubsub_unsubscribe((pubsub_sub_pt)((fio_ls_s *)subscription_id)->obj);
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 *websocket_udata(ws_s *ws) { return ws->udata; }
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, void *data, size_t size, uint8_t is_text) {
678
- if (sock_isvalid(ws->fd)) {
679
- websocket_write_impl(ws->fd, data, size, is_text, 1, 1, ws->is_client);
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
- sock_write2(.uuid = ws->fd, .buffer = "\x88\x00", .length = 2,
687
- .dealloc = SOCK_DEALLOC_NOOP);
688
- sock_close(ws->fd);
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
- }