iodine 0.4.8 → 0.4.10
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/.gitignore +1 -0
- data/CHANGELOG.md +26 -0
- data/README.md +18 -12
- data/SPEC-Websocket-Draft.md +9 -5
- data/bin/ws-echo +3 -0
- data/examples/config.ru +0 -1
- data/examples/echo.ru +3 -1
- data/examples/redis.ru +0 -1
- data/ext/iodine/base64.c +97 -105
- data/ext/iodine/defer.c +16 -1
- data/ext/iodine/defer.h +10 -0
- data/ext/iodine/evio.c +35 -13
- data/ext/iodine/extconf.rb +1 -1
- data/ext/iodine/facil.c +12 -1
- data/ext/iodine/facil.h +3 -1
- data/ext/iodine/fio2resp.c +71 -0
- data/ext/iodine/fio2resp.h +50 -0
- data/ext/iodine/fio_cli_helper.c +404 -0
- data/ext/iodine/fio_cli_helper.h +152 -0
- data/ext/iodine/fiobj.h +631 -0
- data/ext/iodine/fiobj_alloc.c +81 -0
- data/ext/iodine/fiobj_ary.c +290 -0
- data/ext/iodine/fiobj_generic.c +260 -0
- data/ext/iodine/fiobj_hash.c +447 -0
- data/ext/iodine/fiobj_io.c +58 -0
- data/ext/iodine/fiobj_json.c +779 -0
- data/ext/iodine/fiobj_misc.c +213 -0
- data/ext/iodine/fiobj_numbers.c +113 -0
- data/ext/iodine/fiobj_primitives.c +98 -0
- data/ext/iodine/fiobj_str.c +261 -0
- data/ext/iodine/fiobj_sym.c +213 -0
- data/ext/iodine/fiobj_tests.c +474 -0
- data/ext/iodine/fiobj_types.h +290 -0
- data/ext/iodine/http1.c +54 -36
- data/ext/iodine/http1_parser.c +143 -35
- data/ext/iodine/http1_parser.h +6 -3
- data/ext/iodine/http1_response.c +0 -1
- data/ext/iodine/http_response.c +1 -1
- data/ext/iodine/iodine.c +20 -4
- data/ext/iodine/iodine_protocol.c +5 -4
- data/ext/iodine/iodine_pubsub.c +1 -1
- data/ext/iodine/random.c +5 -5
- data/ext/iodine/sha1.c +5 -8
- data/ext/iodine/sha2.c +8 -11
- data/ext/iodine/sha2.h +3 -3
- data/ext/iodine/sock.c +29 -31
- data/ext/iodine/websocket_parser.h +428 -0
- data/ext/iodine/websockets.c +112 -377
- data/ext/iodine/xor-crypt.c +16 -12
- data/lib/iodine/version.rb +1 -1
- metadata +21 -3
- data/ext/iodine/empty.h +0 -26
data/ext/iodine/websockets.c
CHANGED
@@ -7,6 +7,7 @@ Feel free to copy, use and enjoy according to the license provided.
|
|
7
7
|
#include "spnlock.inc"
|
8
8
|
|
9
9
|
#include "fio_list.h"
|
10
|
+
#include "fiobj.h"
|
10
11
|
|
11
12
|
#include "bscrypt.h"
|
12
13
|
#include "pubsub.h"
|
@@ -18,6 +19,8 @@ Feel free to copy, use and enjoy according to the license provided.
|
|
18
19
|
#include <string.h>
|
19
20
|
#include <strings.h>
|
20
21
|
|
22
|
+
#include "websocket_parser.h"
|
23
|
+
|
21
24
|
#if !defined(__BIG_ENDIAN__) && !defined(__LITTLE_ENDIAN__)
|
22
25
|
#include <endian.h>
|
23
26
|
#if !defined(__BIG_ENDIAN__) && !defined(__LITTLE_ENDIAN__) && \
|
@@ -61,7 +64,7 @@ the code probably wouldn't offer a high performance boost.
|
|
61
64
|
struct buffer_s create_ws_buffer(ws_s *owner) {
|
62
65
|
(void)(owner);
|
63
66
|
struct buffer_s buff;
|
64
|
-
buff.size =
|
67
|
+
buff.size = WS_INITIAL_BUFFER_SIZE;
|
65
68
|
buff.data = malloc(buff.size);
|
66
69
|
return buff;
|
67
70
|
}
|
@@ -111,40 +114,16 @@ struct Websocket {
|
|
111
114
|
size_t max_msg_size;
|
112
115
|
/** active pub/sub subscriptions */
|
113
116
|
fio_list_s subscriptions;
|
114
|
-
/**
|
117
|
+
/** socket buffer. */
|
115
118
|
struct buffer_s buffer;
|
116
|
-
/**
|
119
|
+
/** data length (how much of the buffer actually used). */
|
117
120
|
size_t length;
|
118
|
-
/**
|
119
|
-
|
120
|
-
|
121
|
-
|
122
|
-
|
123
|
-
|
124
|
-
} psize;
|
125
|
-
size_t length;
|
126
|
-
size_t received;
|
127
|
-
char mask[4];
|
128
|
-
struct {
|
129
|
-
unsigned op_code : 4;
|
130
|
-
unsigned rsv3 : 1;
|
131
|
-
unsigned rsv2 : 1;
|
132
|
-
unsigned rsv1 : 1;
|
133
|
-
unsigned fin : 1;
|
134
|
-
} head, head2;
|
135
|
-
struct {
|
136
|
-
unsigned size : 7;
|
137
|
-
unsigned masked : 1;
|
138
|
-
} sdata;
|
139
|
-
struct {
|
140
|
-
unsigned has_mask : 1;
|
141
|
-
unsigned at_mask : 2;
|
142
|
-
unsigned has_len : 1;
|
143
|
-
unsigned at_len : 3;
|
144
|
-
unsigned has_head : 1;
|
145
|
-
} state;
|
146
|
-
unsigned client : 1;
|
147
|
-
} parser;
|
121
|
+
/** message buffer. */
|
122
|
+
fiobj_s *msg;
|
123
|
+
/** latest text state. */
|
124
|
+
uint8_t is_text;
|
125
|
+
/** websocket connection type. */
|
126
|
+
uint8_t is_client;
|
148
127
|
};
|
149
128
|
|
150
129
|
/**
|
@@ -152,15 +131,6 @@ The Websocket Protocol Identifying String. Used for the `each` function.
|
|
152
131
|
*/
|
153
132
|
char *WEBSOCKET_ID_STR = "websockets";
|
154
133
|
|
155
|
-
/**
|
156
|
-
A thread localized buffer used for reading and parsing data from the socket.
|
157
|
-
*/
|
158
|
-
#define WEBSOCKET_READ_MAX 4096
|
159
|
-
static __thread struct {
|
160
|
-
int pos;
|
161
|
-
char buffer[WEBSOCKET_READ_MAX];
|
162
|
-
} read_buffer;
|
163
|
-
|
164
134
|
/* *****************************************************************************
|
165
135
|
Create/Destroy the websocket subscription objects
|
166
136
|
***************************************************************************** */
|
@@ -190,6 +160,52 @@ static inline void clear_subscriptions(ws_s *ws) {
|
|
190
160
|
}
|
191
161
|
}
|
192
162
|
|
163
|
+
/* *****************************************************************************
|
164
|
+
Callbacks - Required functions for websocket_parser.h
|
165
|
+
***************************************************************************** */
|
166
|
+
|
167
|
+
static void websocket_on_unwrapped(void *udata, void *msg, uint64_t len,
|
168
|
+
char first, char last, char text,
|
169
|
+
unsigned char rsv) {
|
170
|
+
ws_s *ws = udata;
|
171
|
+
if (last && first) {
|
172
|
+
ws->on_message(ws, msg, len, (uint8_t)text);
|
173
|
+
return;
|
174
|
+
}
|
175
|
+
if (first) {
|
176
|
+
ws->is_text = (uint8_t)text;
|
177
|
+
if (ws->msg == NULL)
|
178
|
+
ws->msg = fiobj_str_buf(len);
|
179
|
+
fiobj_str_resize(ws->msg, 0);
|
180
|
+
}
|
181
|
+
fiobj_str_write(ws->msg, msg, len);
|
182
|
+
if (last) {
|
183
|
+
fio_cstr_s s = fiobj_obj2cstr(ws->msg);
|
184
|
+
ws->on_message(ws, (char *)s.data, s.len, ws->is_text);
|
185
|
+
}
|
186
|
+
|
187
|
+
(void)rsv;
|
188
|
+
}
|
189
|
+
static void websocket_on_protocol_ping(void *udata, void *msg_, uint64_t len) {
|
190
|
+
ws_s *ws = udata;
|
191
|
+
uint16_t *msg = msg_;
|
192
|
+
msg[-1] = *((uint16_t *)"\x89\x00");
|
193
|
+
sock_write2(.uuid = ws->fd, .buffer = (void *)(msg - 1), .length = 2 + len);
|
194
|
+
}
|
195
|
+
static void websocket_on_protocol_pong(void *udata, void *msg, uint64_t len) {
|
196
|
+
(void)len;
|
197
|
+
(void)msg;
|
198
|
+
(void)udata;
|
199
|
+
}
|
200
|
+
static void websocket_on_protocol_close(void *udata) {
|
201
|
+
ws_s *ws = udata;
|
202
|
+
sock_close(ws->fd);
|
203
|
+
}
|
204
|
+
static void websocket_on_protocol_error(void *udata) {
|
205
|
+
ws_s *ws = udata;
|
206
|
+
sock_close(ws->fd);
|
207
|
+
}
|
208
|
+
|
193
209
|
/*******************************************************************************
|
194
210
|
The Websocket Protocol implementation
|
195
211
|
*/
|
@@ -225,247 +241,45 @@ static void on_shutdown(intptr_t fd, protocol_s *ws) {
|
|
225
241
|
((ws_s *)ws)->on_shutdown((ws_s *)ws);
|
226
242
|
}
|
227
243
|
|
228
|
-
|
229
|
-
static void websocket_write_impl(intptr_t fd, void *data, size_t len, char text,
|
230
|
-
char first, char last, char client);
|
231
|
-
static size_t websocket_encode(void *buff, void *data, size_t len, char text,
|
232
|
-
char first, char last, char client);
|
244
|
+
/************** new implementation */
|
233
245
|
|
234
|
-
|
235
|
-
|
236
|
-
#define ws ((ws_s *)_ws)
|
246
|
+
static void on_data(intptr_t sockfd, protocol_s *ws_) {
|
247
|
+
ws_s *const ws = (ws_s *)ws_;
|
237
248
|
if (ws == NULL || ws->protocol.service != WEBSOCKET_ID_STR)
|
238
249
|
return;
|
239
|
-
|
240
|
-
|
241
|
-
|
242
|
-
|
243
|
-
if (
|
250
|
+
struct websocket_packet_info_s info =
|
251
|
+
websocket_buffer_peek(ws->buffer.data, ws->length);
|
252
|
+
const uint64_t raw_length = info.packet_length + info.head_length;
|
253
|
+
/* test expected data amount */
|
254
|
+
if (ws->max_msg_size < raw_length) {
|
255
|
+
/* too big */
|
256
|
+
websocket_close(ws);
|
244
257
|
return;
|
245
|
-
|
246
|
-
|
247
|
-
|
248
|
-
|
249
|
-
|
250
|
-
|
251
|
-
//
|
252
|
-
if (!(*(char *)(&ws->parser.head2))) {
|
253
|
-
ws->parser.head2 = ws->parser.head;
|
254
|
-
}
|
255
|
-
// advance
|
256
|
-
read_buffer.pos++;
|
257
|
-
// go back to the `while` head, to review if there's more data
|
258
|
-
continue;
|
259
|
-
}
|
260
|
-
|
261
|
-
// save the mask and size information
|
262
|
-
if (!ws->parser.state.at_len && !ws->parser.state.has_len) {
|
263
|
-
// uint8_t tmp = ws->parser.sdata.masked;
|
264
|
-
*((char *)(&(ws->parser.sdata))) = read_buffer.buffer[read_buffer.pos];
|
265
|
-
// ws->parser.sdata.masked |= tmp;
|
266
|
-
// set length
|
267
|
-
ws->parser.state.at_len =
|
268
|
-
(ws->parser.sdata.size == 127 ? 7
|
269
|
-
: ws->parser.sdata.size == 126 ? 1 : 0);
|
270
|
-
if (!ws->parser.state.at_len) {
|
271
|
-
ws->parser.length = ws->parser.sdata.size;
|
272
|
-
ws->parser.state.has_len = 1;
|
273
|
-
}
|
274
|
-
read_buffer.pos++;
|
275
|
-
continue;
|
276
|
-
}
|
277
|
-
|
278
|
-
// check that if we need to collect the length data
|
279
|
-
if (!ws->parser.state.has_len) {
|
280
|
-
// avoiding a loop so we don't mixup the meaning of "continue" and
|
281
|
-
// "break"
|
282
|
-
collect_len:
|
283
|
-
////////// NOTICE: Network Byte Order requires us to translate the data
|
284
|
-
#ifdef __BIG_ENDIAN__
|
285
|
-
if ((ws->parser.state.at_len == 1 && ws->parser.sdata.size == 126) ||
|
286
|
-
(ws->parser.state.at_len == 7 && ws->parser.sdata.size == 127)) {
|
287
|
-
ws->parser.psize.bytes[ws->parser.state.at_len] =
|
288
|
-
read_buffer.buffer[read_buffer.pos++];
|
289
|
-
ws->parser.state.has_len = 1;
|
290
|
-
ws->parser.length = (ws->parser.sdata.size == 126)
|
291
|
-
? ws->parser.psize.len1
|
292
|
-
: ws->parser.psize.len2;
|
293
|
-
} else {
|
294
|
-
ws->parser.psize.bytes[ws->parser.state.at_len++] =
|
295
|
-
read_buffer.buffer[read_buffer.pos++];
|
296
|
-
if (read_buffer.pos < len)
|
297
|
-
goto collect_len;
|
298
|
-
}
|
299
|
-
#else
|
300
|
-
if (ws->parser.state.at_len == 0) {
|
301
|
-
ws->parser.psize.bytes[ws->parser.state.at_len] =
|
302
|
-
read_buffer.buffer[read_buffer.pos++];
|
303
|
-
ws->parser.state.has_len = 1;
|
304
|
-
ws->parser.length = (ws->parser.sdata.size == 126)
|
305
|
-
? ws->parser.psize.len1
|
306
|
-
: ws->parser.psize.len2;
|
307
|
-
} else {
|
308
|
-
ws->parser.psize.bytes[ws->parser.state.at_len--] =
|
309
|
-
read_buffer.buffer[read_buffer.pos++];
|
310
|
-
if (read_buffer.pos < len)
|
311
|
-
goto collect_len;
|
312
|
-
}
|
313
|
-
#endif
|
314
|
-
// check message size limit
|
315
|
-
if (ws->max_msg_size <
|
316
|
-
ws->length + (ws->parser.length - ws->parser.received)) {
|
317
|
-
// close connection!
|
318
|
-
fprintf(stderr, "ERROR Websocket: Payload too big, review limits.\n");
|
319
|
-
sock_close(sockfd);
|
320
|
-
return;
|
321
|
-
}
|
322
|
-
continue;
|
323
|
-
}
|
324
|
-
|
325
|
-
// check that the data is masked and that we didn't colleced the mask yet
|
326
|
-
if (ws->parser.sdata.masked && !(ws->parser.state.has_mask)) {
|
327
|
-
// avoiding a loop so we don't mixup the meaning of "continue" and "break"
|
328
|
-
collect_mask:
|
329
|
-
if (ws->parser.state.at_mask == 3) {
|
330
|
-
ws->parser.mask[ws->parser.state.at_mask] =
|
331
|
-
read_buffer.buffer[read_buffer.pos++];
|
332
|
-
ws->parser.state.has_mask = 1;
|
333
|
-
ws->parser.state.at_mask = 0;
|
334
|
-
} else {
|
335
|
-
ws->parser.mask[ws->parser.state.at_mask++] =
|
336
|
-
read_buffer.buffer[read_buffer.pos++];
|
337
|
-
if (read_buffer.pos < len)
|
338
|
-
goto collect_mask;
|
339
|
-
else
|
340
|
-
continue;
|
341
|
-
}
|
342
|
-
// since it's possible that there's no more data (0 length frame),
|
343
|
-
// we don't use `continue` (check while loop) and we process what we
|
344
|
-
// have.
|
345
|
-
}
|
346
|
-
|
347
|
-
// Now that we know everything about the frame, let's collect the data
|
348
|
-
|
349
|
-
// How much data in the buffer is part of the frame?
|
350
|
-
data_len = len - read_buffer.pos;
|
351
|
-
if (data_len + ws->parser.received > ws->parser.length)
|
352
|
-
data_len = ws->parser.length - ws->parser.received;
|
353
|
-
|
354
|
-
// a note about unmasking: since ws->parser.state.at_mask is only 2 bits,
|
355
|
-
// it will wrap around (i.e. 3++ == 0), so no modulus is required :-)
|
356
|
-
// unmask:
|
357
|
-
if (ws->parser.sdata.masked) {
|
358
|
-
for (int i = 0; i < data_len; i++) {
|
359
|
-
read_buffer.buffer[i + read_buffer.pos] ^=
|
360
|
-
ws->parser.mask[ws->parser.state.at_mask++];
|
361
|
-
}
|
362
|
-
} else if (ws->parser.client == 0) {
|
363
|
-
// enforce masking unless acting as client, also for security reasons...
|
364
|
-
fprintf(stderr, "ERROR Websockets: unmasked frame, disconnecting.\n");
|
365
|
-
sock_close(sockfd);
|
366
|
-
return;
|
367
|
-
}
|
368
|
-
// Copy the data to the Websocket buffer - only if it's a user message
|
369
|
-
if (data_len &&
|
370
|
-
(ws->parser.head.op_code == 1 || ws->parser.head.op_code == 2 ||
|
371
|
-
(!ws->parser.head.op_code &&
|
372
|
-
(ws->parser.head2.op_code == 1 || ws->parser.head2.op_code == 2)))) {
|
373
|
-
// review and resize the buffer's capacity - it can only grow.
|
374
|
-
if (ws->length + ws->parser.length - ws->parser.received >
|
375
|
-
ws->buffer.size) {
|
376
|
-
ws->buffer = resize_ws_buffer(ws, ws->buffer);
|
377
|
-
if (!ws->buffer.data) {
|
378
|
-
// no memory.
|
379
|
-
websocket_close(ws);
|
380
|
-
return;
|
381
|
-
}
|
382
|
-
}
|
383
|
-
// copy here
|
384
|
-
memcpy((uint8_t *)ws->buffer.data + ws->length,
|
385
|
-
read_buffer.buffer + read_buffer.pos, data_len);
|
386
|
-
ws->length += data_len;
|
387
|
-
}
|
388
|
-
// set the frame's data received so far (copied or not)
|
389
|
-
ws->parser.received += data_len;
|
390
|
-
|
391
|
-
// check that we have collected the whole of the frame.
|
392
|
-
if (ws->parser.length > ws->parser.received) {
|
393
|
-
read_buffer.pos += data_len;
|
394
|
-
// fprintf(stderr, "%p websocket has %lu out of %lu\n", (void *)ws,
|
395
|
-
// ws->parser.received, ws->parser.length);
|
396
|
-
continue;
|
397
|
-
}
|
398
|
-
|
399
|
-
// we have the whole frame, time to process the data.
|
400
|
-
// pings, pongs and other non-user messages are handled independently.
|
401
|
-
if (ws->parser.head.op_code == 0 || ws->parser.head.op_code == 1 ||
|
402
|
-
ws->parser.head.op_code == 2) {
|
403
|
-
/* a user data frame - make sure we got the `fin` flag, or an error
|
404
|
-
* occured */
|
405
|
-
if (!ws->parser.head.fin) {
|
406
|
-
/* This frame was a partial message. */
|
407
|
-
goto reset_state;
|
408
|
-
}
|
409
|
-
/* This was the last frame */
|
410
|
-
if (ws->on_message) /* call the on_message callback */
|
411
|
-
ws->on_message(ws, ws->buffer.data, ws->length,
|
412
|
-
ws->parser.head2.op_code == 1);
|
413
|
-
goto reset_parser;
|
414
|
-
} else if (ws->parser.head.op_code == 8) {
|
415
|
-
/* op-code == close */
|
258
|
+
}
|
259
|
+
/* test buffer capacity */
|
260
|
+
if (raw_length >= ws->buffer.size) {
|
261
|
+
ws->buffer.size = (size_t)raw_length;
|
262
|
+
ws->buffer = resize_ws_buffer(ws, ws->buffer);
|
263
|
+
if (!ws->buffer.data) {
|
264
|
+
// no memory.
|
416
265
|
websocket_close(ws);
|
417
|
-
if (ws->parser.head2.op_code == ws->parser.head.op_code)
|
418
|
-
goto reset_parser;
|
419
|
-
} else if (ws->parser.head.op_code == 9) {
|
420
|
-
/* ping */
|
421
|
-
// write Pong - including ping data...
|
422
|
-
websocket_write_impl(sockfd, read_buffer.buffer + read_buffer.pos,
|
423
|
-
data_len, 10, 1, 1, ws->parser.client);
|
424
|
-
if (ws->parser.head2.op_code == ws->parser.head.op_code)
|
425
|
-
goto reset_parser;
|
426
|
-
} else if (ws->parser.head.op_code == 10) {
|
427
|
-
/* pong */
|
428
|
-
// do nothing... almost
|
429
|
-
if (ws->parser.head2.op_code == ws->parser.head.op_code)
|
430
|
-
goto reset_parser;
|
431
|
-
} else if (ws->parser.head.op_code > 2 && ws->parser.head.op_code < 8) {
|
432
|
-
/* future control frames. ignore. */
|
433
|
-
if (ws->parser.head2.op_code == ws->parser.head.op_code)
|
434
|
-
goto reset_parser;
|
435
|
-
} else {
|
436
|
-
/* WTF? */
|
437
|
-
// fprintf(stderr, "%p websocket reached a WTF?! state..."
|
438
|
-
// "op1: %i , op2: %i\n",
|
439
|
-
// (void *)ws, ws->parser.head.op_code,
|
440
|
-
// ws->parser.head2.op_code);
|
441
|
-
fprintf(stderr, "ERROR Websockets: protocol error, disconnecting.\n");
|
442
|
-
sock_close(sockfd);
|
443
266
|
return;
|
444
267
|
}
|
268
|
+
}
|
445
269
|
|
446
|
-
|
447
|
-
|
448
|
-
|
449
|
-
|
450
|
-
ws->parser.sdata.masked = 0;
|
451
|
-
reset_state:
|
452
|
-
// move the pos marker along - in case we have more then one frame in the
|
453
|
-
// buffer
|
454
|
-
read_buffer.pos += data_len;
|
455
|
-
// reset parser state
|
456
|
-
ws->parser.state.has_len = 0;
|
457
|
-
ws->parser.state.at_len = 0;
|
458
|
-
ws->parser.state.has_mask = 0;
|
459
|
-
ws->parser.state.at_mask = 0;
|
460
|
-
ws->parser.state.has_head = 0;
|
461
|
-
ws->parser.sdata.size = 0;
|
462
|
-
*((char *)(&(ws->parser.head))) = 0;
|
463
|
-
ws->parser.received = ws->parser.length = ws->parser.psize.len2 = data_len =
|
464
|
-
0;
|
270
|
+
ssize_t len = sock_read(sockfd, (uint8_t *)ws->buffer.data + ws->length,
|
271
|
+
ws->buffer.size - ws->length);
|
272
|
+
if (len <= 0) {
|
273
|
+
return;
|
465
274
|
}
|
275
|
+
ws->length += len;
|
276
|
+
ws->length = websocket_consume(ws->buffer.data, ws->length, ws, 1);
|
277
|
+
|
466
278
|
facil_force_event(sockfd, FIO_EVENT_ON_DATA);
|
467
|
-
#undef ws
|
468
279
|
}
|
280
|
+
/* later */
|
281
|
+
static void websocket_write_impl(intptr_t fd, void *data, size_t len, char text,
|
282
|
+
char first, char last, char client);
|
469
283
|
|
470
284
|
/*******************************************************************************
|
471
285
|
Create/Destroy the websocket object
|
@@ -482,6 +296,7 @@ static ws_s *new_websocket() {
|
|
482
296
|
.protocol.on_ready = on_ready,
|
483
297
|
.protocol.on_shutdown = on_shutdown,
|
484
298
|
.subscriptions = FIO_LIST_INIT_STATIC(ws->subscriptions),
|
299
|
+
.is_client = 0,
|
485
300
|
};
|
486
301
|
return ws;
|
487
302
|
}
|
@@ -489,6 +304,8 @@ static void destroy_ws(ws_s *ws) {
|
|
489
304
|
clear_subscriptions(ws);
|
490
305
|
if (ws->on_close)
|
491
306
|
ws->on_close(ws);
|
307
|
+
if (ws->msg)
|
308
|
+
fiobj_free(ws->msg);
|
492
309
|
free_ws_buffer(ws, ws->buffer);
|
493
310
|
free(ws);
|
494
311
|
}
|
@@ -526,115 +343,27 @@ Writing to the Websocket
|
|
526
343
|
(((i)&0xFF0000ULL) << 24) | (((i)&0xFF000000ULL) << 8) | \
|
527
344
|
(((i)&0xFF00000000ULL) >> 8) | (((i)&0xFF0000000000ULL) >> 24) | \
|
528
345
|
(((i)&0xFF000000000000ULL) >> 40) | (((i)&0xFF00000000000000ULL) >> 56))
|
529
|
-
|
530
346
|
#endif
|
531
347
|
|
532
|
-
static void websocket_mask(void *dest, void *data, size_t len) {
|
533
|
-
/* a semi-random 4 byte mask */
|
534
|
-
uint32_t mask = ((rand() << 7) ^ ((uintptr_t)dest >> 13));
|
535
|
-
/* place mask at head of data */
|
536
|
-
dest = (uint8_t *)dest + 4;
|
537
|
-
memcpy(dest, &mask, 4);
|
538
|
-
/* TODO: optimize this */
|
539
|
-
for (size_t i = 0; i < len; i++) {
|
540
|
-
((uint8_t *)dest)[i] = ((uint8_t *)data)[i] ^ ((uint8_t *)(&mask))[i & 3];
|
541
|
-
}
|
542
|
-
}
|
543
|
-
static size_t websocket_encode(void *buff, void *data, size_t len, char text,
|
544
|
-
char first, char last, char client) {
|
545
|
-
if (len < 126) {
|
546
|
-
struct {
|
547
|
-
unsigned op_code : 4;
|
548
|
-
unsigned rsv3 : 1;
|
549
|
-
unsigned rsv2 : 1;
|
550
|
-
unsigned rsv1 : 1;
|
551
|
-
unsigned fin : 1;
|
552
|
-
unsigned size : 7;
|
553
|
-
unsigned masked : 1;
|
554
|
-
} head = {.op_code = (first ? (!text ? 2 : text) : 0),
|
555
|
-
.fin = last,
|
556
|
-
.size = len,
|
557
|
-
.masked = client};
|
558
|
-
memcpy(buff, &head, 2);
|
559
|
-
if (client) {
|
560
|
-
websocket_mask((uint8_t *)buff + 2, data, len);
|
561
|
-
len += 4;
|
562
|
-
} else
|
563
|
-
memcpy((uint8_t *)buff + 2, data, len);
|
564
|
-
return len + 2;
|
565
|
-
} else if (len < (1UL << 16)) {
|
566
|
-
/* head is 4 bytes */
|
567
|
-
struct {
|
568
|
-
unsigned op_code : 4;
|
569
|
-
unsigned rsv3 : 1;
|
570
|
-
unsigned rsv2 : 1;
|
571
|
-
unsigned rsv1 : 1;
|
572
|
-
unsigned fin : 1;
|
573
|
-
unsigned size : 7;
|
574
|
-
unsigned masked : 1;
|
575
|
-
unsigned length : 16;
|
576
|
-
} head = {.op_code = (first ? (text ? 1 : 2) : 0),
|
577
|
-
.fin = last,
|
578
|
-
.size = 126,
|
579
|
-
.masked = client,
|
580
|
-
.length = htons(len)};
|
581
|
-
memcpy(buff, &head, 4);
|
582
|
-
if (client) {
|
583
|
-
websocket_mask((uint8_t *)buff + 4, data, len);
|
584
|
-
len += 4;
|
585
|
-
} else
|
586
|
-
memcpy((uint8_t *)buff + 4, data, len);
|
587
|
-
return len + 4;
|
588
|
-
}
|
589
|
-
/* Really Long Message */
|
590
|
-
struct {
|
591
|
-
unsigned op_code : 4;
|
592
|
-
unsigned rsv3 : 1;
|
593
|
-
unsigned rsv2 : 1;
|
594
|
-
unsigned rsv1 : 1;
|
595
|
-
unsigned fin : 1;
|
596
|
-
unsigned size : 7;
|
597
|
-
unsigned masked : 1;
|
598
|
-
} head = {
|
599
|
-
.op_code = (first ? (text ? 1 : 2) : 0),
|
600
|
-
.fin = last,
|
601
|
-
.size = 127,
|
602
|
-
.masked = client,
|
603
|
-
};
|
604
|
-
memcpy(buff, &head, 2);
|
605
|
-
((size_t *)((uint8_t *)buff + 2))[0] = bswap64(len);
|
606
|
-
if (client) {
|
607
|
-
websocket_mask((uint8_t *)buff + 10, data, len);
|
608
|
-
len += 4;
|
609
|
-
} else
|
610
|
-
memcpy((uint8_t *)buff + 10, data, len);
|
611
|
-
return len + 10;
|
612
|
-
}
|
613
|
-
|
614
348
|
static void websocket_write_impl(intptr_t fd, void *data, size_t len,
|
615
349
|
char text, /* TODO: add client masking */
|
616
350
|
char first, char last, char client) {
|
617
|
-
if (len <
|
351
|
+
if (len < (BUFFER_PACKET_SIZE - 16)) {
|
618
352
|
sock_buffer_s *sbuff = sock_buffer_checkout();
|
353
|
+
|
619
354
|
sbuff->len =
|
620
|
-
|
355
|
+
(client ? websocket_client_wrap(sbuff->buf, data, len, (text ? 1 : 2),
|
356
|
+
first, last, 0)
|
357
|
+
: websocket_server_wrap(sbuff->buf, data, len, (text ? 1 : 2),
|
358
|
+
first, last, 0));
|
621
359
|
sock_buffer_send(fd, sbuff);
|
622
|
-
// // was:
|
623
|
-
// char buff[len + (client ? 6 : 2)];
|
624
|
-
// len = websocket_encode(buff, data, len, text, first, last, client);
|
625
|
-
// sock_write(fd, buff, len);
|
626
360
|
} else if (len <= WS_MAX_FRAME_SIZE) {
|
627
|
-
|
628
|
-
|
629
|
-
|
630
|
-
|
631
|
-
|
632
|
-
|
633
|
-
sock_buffer_s *sbuff = sock_buffer_checkout();
|
634
|
-
sbuff->len =
|
635
|
-
websocket_encode(sbuff->buf, data, len, text, first, last, client);
|
636
|
-
sock_buffer_send(fd, sbuff);
|
637
|
-
}
|
361
|
+
void *buff = malloc(len + 16);
|
362
|
+
len = (client ? websocket_client_wrap(buff, data, len, (text ? 1 : 2),
|
363
|
+
first, last, 0)
|
364
|
+
: websocket_server_wrap(buff, data, len, (text ? 1 : 2),
|
365
|
+
first, last, 0));
|
366
|
+
sock_write2(.uuid = fd, .buffer = buff, .length = len, .move = 1);
|
638
367
|
} else {
|
639
368
|
/* frame fragmentation is better for large data then large frames */
|
640
369
|
while (len > WS_MAX_FRAME_SIZE) {
|
@@ -989,6 +718,8 @@ cleanup:
|
|
989
718
|
// call the on_open callback
|
990
719
|
if (settings.on_open) {
|
991
720
|
defer(deferred_on_open, (void *)settings.on_open, ws);
|
721
|
+
} else {
|
722
|
+
facil_protocol_unlock(&ws->protocol, FIO_PR_LOCK_TASK);
|
992
723
|
}
|
993
724
|
return 0;
|
994
725
|
}
|
@@ -1013,7 +744,7 @@ void *websocket_udata_set(ws_s *ws, void *udata) {
|
|
1013
744
|
/** Writes data to the websocket. Returns -1 on failure (0 on success). */
|
1014
745
|
int websocket_write(ws_s *ws, void *data, size_t size, uint8_t is_text) {
|
1015
746
|
if (sock_isvalid(ws->fd)) {
|
1016
|
-
websocket_write_impl(ws->fd, data, size, is_text, 1, 1, ws->
|
747
|
+
websocket_write_impl(ws->fd, data, size, is_text, 1, 1, ws->is_client);
|
1017
748
|
return 0;
|
1018
749
|
}
|
1019
750
|
return -1;
|
@@ -1135,7 +866,7 @@ static void ws_finish_multi_write(intptr_t fd, void *arg) {
|
|
1135
866
|
|
1136
867
|
static void ws_direct_multi_write(intptr_t fd, protocol_s *_ws, void *arg) {
|
1137
868
|
struct websocket_multi_write *multi = arg;
|
1138
|
-
if (((ws_s *)(_ws))->
|
869
|
+
if (((ws_s *)(_ws))->is_client != multi->as_client)
|
1139
870
|
return;
|
1140
871
|
spn_lock(&multi->lock);
|
1141
872
|
multi->count += 1;
|
@@ -1146,7 +877,7 @@ static void ws_direct_multi_write(intptr_t fd, protocol_s *_ws, void *arg) {
|
|
1146
877
|
|
1147
878
|
static void ws_check_multi_write(intptr_t fd, protocol_s *_ws, void *arg) {
|
1148
879
|
struct websocket_multi_write *multi = arg;
|
1149
|
-
if (((ws_s *)(_ws))->
|
880
|
+
if (((ws_s *)(_ws))->is_client != multi->as_client)
|
1150
881
|
return;
|
1151
882
|
if (multi->if_callback((void *)_ws, multi->arg))
|
1152
883
|
ws_direct_multi_write(fd, _ws, arg);
|
@@ -1164,8 +895,12 @@ int websocket_write_each(struct websocket_write_each_args_s args) {
|
|
1164
895
|
return -1;
|
1165
896
|
}
|
1166
897
|
*multi = (struct websocket_multi_write){
|
1167
|
-
.length =
|
1168
|
-
|
898
|
+
.length =
|
899
|
+
(args.as_client
|
900
|
+
? websocket_client_wrap(multi->buffer, args.data, args.length,
|
901
|
+
args.is_text ? 1 : 2, 1, 1, 0)
|
902
|
+
: websocket_server_wrap(multi->buffer, args.data, args.length,
|
903
|
+
args.is_text ? 1 : 2, 1, 1, 0)),
|
1169
904
|
.if_callback = args.filter,
|
1170
905
|
.on_finished = args.on_finished,
|
1171
906
|
.arg = args.arg,
|