iodine 0.4.2 → 0.4.3

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 CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA1:
3
- metadata.gz: 375dff05d190f0bd67e614a7e17ca4f260e94931
4
- data.tar.gz: 40214b68ddd02635863435bd25aa513165815733
3
+ metadata.gz: 56640878e0c4cc4794f8cf3747a398a51847b0ae
4
+ data.tar.gz: 0c58b292c6002ea03e068eba06f5aa75c449d236
5
5
  SHA512:
6
- metadata.gz: 7d65d83cb40603cb233af7be43ed1eed8151dc3530acf850b112583a06e77c026c9415e70ad4daeffe8c513a5164cdb2eddead101d35c08c2ffb60cbcae69d8a
7
- data.tar.gz: 1910c42c7e776bd60cb564df4a09f7235a002833420cc81bcc5b17d14e3b70b7f78eaa685fbcd85058e552c249ae767bec7d3caf00478fa30979b1b146746ccb
6
+ metadata.gz: 193156a5098163d096af9c182ed20b7a6977234575d4fd49a81ba07d1f44dc677f8772bd6c873f79f0466b28037290463d070473eb057e4c522127e0a51d5e8a
7
+ data.tar.gz: ab58390cfd148cd7fa481cec9f3db47ccbe64188e993e9c34baaaab2b0eaaf51cb65c459d6a9ec0ee1315a0dcf0536d8931b72aae0d1fd2c0dd743216788aab0
@@ -6,6 +6,15 @@ Please notice that this change log contains changes for upcoming releases as wel
6
6
 
7
7
  ## Changes:
8
8
 
9
+ ***
10
+
11
+ #### Change log v.0.4.3
12
+
13
+ This release is a ghost hunt release, attempting to find an issue noticed only during when deploying in the Heroku production environment.
14
+
15
+ **Fix**: fixed a possible issue in fragmented pipelined Websocket messages. This
16
+
17
+
9
18
  ***
10
19
 
11
20
  #### Change log v.0.4.2
@@ -170,6 +170,24 @@ void evio_on_close(void *arg) { sock_force_close((intptr_t)arg); }
170
170
  void evio_on_error(void *arg) { sock_force_close((intptr_t)arg); }
171
171
  void evio_on_data(void *arg) { defer(deferred_on_data, arg, NULL); }
172
172
 
173
+ /* *****************************************************************************
174
+ Forcing IO events
175
+ ***************************************************************************** */
176
+
177
+ void facil_force_event(intptr_t uuid, enum facil_io_event ev) {
178
+ switch (ev) {
179
+ case FIO_EVENT_ON_DATA:
180
+ evio_on_data((void *)uuid);
181
+ break;
182
+ case FIO_EVENT_ON_TIMEOUT:
183
+ defer(deferred_ping, (void *)uuid, NULL);
184
+ break;
185
+ case FIO_EVENT_ON_READY:
186
+ evio_on_ready((void *)uuid);
187
+ break;
188
+ }
189
+ }
190
+
173
191
  /* *****************************************************************************
174
192
  Socket callbacks
175
193
  ***************************************************************************** */
@@ -303,6 +303,14 @@ void facil_set_timeout(intptr_t uuid, uint8_t timeout);
303
303
  /** Gets a timeout for a specific connection. Returns 0 if none. */
304
304
  uint8_t facil_get_timeout(intptr_t uuid);
305
305
 
306
+ enum facil_io_event {
307
+ FIO_EVENT_ON_DATA,
308
+ FIO_EVENT_ON_READY,
309
+ FIO_EVENT_ON_TIMEOUT,
310
+ };
311
+ /** Schedules an IO event, even id it did not occur. */
312
+ void facil_force_event(intptr_t uuid, enum facil_io_event);
313
+
306
314
  /* *****************************************************************************
307
315
  Helper API
308
316
  ***************************************************************************** */
@@ -1041,7 +1041,7 @@ after all the data was sent. This is a "busy" wait, polling isn't performed.
1041
1041
  */
1042
1042
  void sock_flush_strong(intptr_t uuid) {
1043
1043
  errno = 0;
1044
- while (sock_flush(uuid) == 0 && !errno)
1044
+ while (sock_flush(uuid) == 0 && errno == 0)
1045
1045
  ;
1046
1046
  }
1047
1047
  /**
@@ -246,245 +246,231 @@ static void on_data(intptr_t sockfd, protocol_s *_ws) {
246
246
  return;
247
247
  ssize_t len = 0;
248
248
  ssize_t data_len = 0;
249
- if (ws->resume_from) {
250
- sock_touch(sockfd);
251
- len = ws->resume_from;
252
- ws->resume_from = 0;
253
- goto resume_parsing;
254
- }
255
- while ((len = sock_read(sockfd, read_buffer.buffer, WEBSOCKET_READ_MAX)) >
256
- 0) {
257
- resume_parsing:
258
- data_len = 0;
259
- read_buffer.pos = 0;
260
- while (read_buffer.pos < len) {
261
- // collect the frame's head
262
- if (!ws->parser.state.has_head) {
263
- ws->parser.state.has_head = 1;
264
- *((char *)(&(ws->parser.head))) = read_buffer.buffer[read_buffer.pos];
265
- // save a copy if it's the first head in a fragmented message
266
- if (!(*(char *)(&ws->parser.head2))) {
267
- ws->parser.head2 = ws->parser.head;
268
- }
269
- // advance
270
- read_buffer.pos++;
271
- // go back to the `while` head, to review if there's more data
272
- continue;
249
+ if ((len = sock_read(sockfd, read_buffer.buffer, WEBSOCKET_READ_MAX)) <= 0)
250
+ return;
251
+ data_len = 0;
252
+ read_buffer.pos = 0;
253
+ while (read_buffer.pos < len) {
254
+ // collect the frame's head
255
+ if (!ws->parser.state.has_head) {
256
+ ws->parser.state.has_head = 1;
257
+ *((char *)(&(ws->parser.head))) = read_buffer.buffer[read_buffer.pos];
258
+ // save a copy if it's the first head in a fragmented message
259
+ if (!(*(char *)(&ws->parser.head2))) {
260
+ ws->parser.head2 = ws->parser.head;
273
261
  }
262
+ // advance
263
+ read_buffer.pos++;
264
+ // go back to the `while` head, to review if there's more data
265
+ continue;
266
+ }
274
267
 
275
- // save the mask and size information
276
- if (!ws->parser.state.at_len && !ws->parser.state.has_len) {
277
- // uint8_t tmp = ws->parser.sdata.masked;
278
- *((char *)(&(ws->parser.sdata))) = read_buffer.buffer[read_buffer.pos];
279
- // ws->parser.sdata.masked |= tmp;
280
- // set length
281
- ws->parser.state.at_len = (ws->parser.sdata.size == 127
282
- ? 7
283
- : ws->parser.sdata.size == 126 ? 1 : 0);
284
- if (!ws->parser.state.at_len) {
285
- ws->parser.length = ws->parser.sdata.size;
286
- ws->parser.state.has_len = 1;
287
- }
288
- read_buffer.pos++;
289
- continue;
268
+ // save the mask and size information
269
+ if (!ws->parser.state.at_len && !ws->parser.state.has_len) {
270
+ // uint8_t tmp = ws->parser.sdata.masked;
271
+ *((char *)(&(ws->parser.sdata))) = read_buffer.buffer[read_buffer.pos];
272
+ // ws->parser.sdata.masked |= tmp;
273
+ // set length
274
+ ws->parser.state.at_len =
275
+ (ws->parser.sdata.size == 127 ? 7
276
+ : ws->parser.sdata.size == 126 ? 1 : 0);
277
+ if (!ws->parser.state.at_len) {
278
+ ws->parser.length = ws->parser.sdata.size;
279
+ ws->parser.state.has_len = 1;
290
280
  }
281
+ read_buffer.pos++;
282
+ continue;
283
+ }
291
284
 
292
- // check that if we need to collect the length data
293
- if (!ws->parser.state.has_len) {
294
- // avoiding a loop so we don't mixup the meaning of "continue" and
295
- // "break"
296
- collect_len:
285
+ // check that if we need to collect the length data
286
+ if (!ws->parser.state.has_len) {
287
+ // avoiding a loop so we don't mixup the meaning of "continue" and
288
+ // "break"
289
+ collect_len:
297
290
  ////////// NOTICE: Network Byte Order requires us to translate the data
298
291
  #ifdef __BIG_ENDIAN__
299
- if ((ws->parser.state.at_len == 1 && ws->parser.sdata.size == 126) ||
300
- (ws->parser.state.at_len == 7 && ws->parser.sdata.size == 127)) {
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
- }
292
+ if ((ws->parser.state.at_len == 1 && ws->parser.sdata.size == 126) ||
293
+ (ws->parser.state.at_len == 7 && ws->parser.sdata.size == 127)) {
294
+ ws->parser.psize.bytes[ws->parser.state.at_len] =
295
+ read_buffer.buffer[read_buffer.pos++];
296
+ ws->parser.state.has_len = 1;
297
+ ws->parser.length = (ws->parser.sdata.size == 126)
298
+ ? ws->parser.psize.len1
299
+ : ws->parser.psize.len2;
300
+ } else {
301
+ ws->parser.psize.bytes[ws->parser.state.at_len++] =
302
+ read_buffer.buffer[read_buffer.pos++];
303
+ if (read_buffer.pos < len)
304
+ goto collect_len;
305
+ }
313
306
  #else
314
- if (ws->parser.state.at_len == 0) {
315
- ws->parser.psize.bytes[ws->parser.state.at_len] =
316
- read_buffer.buffer[read_buffer.pos++];
317
- ws->parser.state.has_len = 1;
318
- ws->parser.length = (ws->parser.sdata.size == 126)
319
- ? ws->parser.psize.len1
320
- : ws->parser.psize.len2;
321
- } else {
322
- ws->parser.psize.bytes[ws->parser.state.at_len--] =
323
- read_buffer.buffer[read_buffer.pos++];
324
- if (read_buffer.pos < len)
325
- goto collect_len;
326
- }
307
+ if (ws->parser.state.at_len == 0) {
308
+ ws->parser.psize.bytes[ws->parser.state.at_len] =
309
+ read_buffer.buffer[read_buffer.pos++];
310
+ ws->parser.state.has_len = 1;
311
+ ws->parser.length = (ws->parser.sdata.size == 126)
312
+ ? ws->parser.psize.len1
313
+ : ws->parser.psize.len2;
314
+ } else {
315
+ ws->parser.psize.bytes[ws->parser.state.at_len--] =
316
+ read_buffer.buffer[read_buffer.pos++];
317
+ if (read_buffer.pos < len)
318
+ goto collect_len;
319
+ }
327
320
  #endif
328
- // check message size limit
329
- if (ws->max_msg_size <
330
- ws->length + (ws->parser.length - ws->parser.received)) {
331
- // close connection!
332
- fprintf(stderr, "ERROR Websocket: Payload too big, review limits.\n");
333
- sock_close(sockfd);
334
- return;
335
- }
336
- continue;
321
+ // check message size limit
322
+ if (ws->max_msg_size <
323
+ ws->length + (ws->parser.length - ws->parser.received)) {
324
+ // close connection!
325
+ fprintf(stderr, "ERROR Websocket: Payload too big, review limits.\n");
326
+ sock_close(sockfd);
327
+ return;
337
328
  }
329
+ continue;
330
+ }
338
331
 
339
- // check that the data is masked and that we didn't colleced the mask yet
340
- if (ws->parser.sdata.masked && !(ws->parser.state.has_mask)) {
341
- // avoiding a loop so we don't mixup the meaning of "continue" and "break"
342
- collect_mask:
343
- if (ws->parser.state.at_mask == 3) {
344
- ws->parser.mask[ws->parser.state.at_mask] =
345
- read_buffer.buffer[read_buffer.pos++];
346
- ws->parser.state.has_mask = 1;
347
- ws->parser.state.at_mask = 0;
348
- } else {
349
- ws->parser.mask[ws->parser.state.at_mask++] =
350
- read_buffer.buffer[read_buffer.pos++];
351
- if (read_buffer.pos < len)
352
- goto collect_mask;
353
- else
354
- continue;
355
- }
356
- // since it's possible that there's no more data (0 length frame),
357
- // we don't use `continue` (check while loop) and we process what we
358
- // have.
332
+ // check that the data is masked and that we didn't colleced the mask yet
333
+ if (ws->parser.sdata.masked && !(ws->parser.state.has_mask)) {
334
+ // avoiding a loop so we don't mixup the meaning of "continue" and "break"
335
+ collect_mask:
336
+ if (ws->parser.state.at_mask == 3) {
337
+ ws->parser.mask[ws->parser.state.at_mask] =
338
+ read_buffer.buffer[read_buffer.pos++];
339
+ ws->parser.state.has_mask = 1;
340
+ ws->parser.state.at_mask = 0;
341
+ } else {
342
+ ws->parser.mask[ws->parser.state.at_mask++] =
343
+ read_buffer.buffer[read_buffer.pos++];
344
+ if (read_buffer.pos < len)
345
+ goto collect_mask;
346
+ else
347
+ continue;
359
348
  }
349
+ // since it's possible that there's no more data (0 length frame),
350
+ // we don't use `continue` (check while loop) and we process what we
351
+ // have.
352
+ }
360
353
 
361
- // Now that we know everything about the frame, let's collect the data
354
+ // Now that we know everything about the frame, let's collect the data
362
355
 
363
- // How much data in the buffer is part of the frame?
364
- data_len = len - read_buffer.pos;
365
- if (data_len + ws->parser.received > ws->parser.length)
366
- data_len = ws->parser.length - ws->parser.received;
356
+ // How much data in the buffer is part of the frame?
357
+ data_len = len - read_buffer.pos;
358
+ if (data_len + ws->parser.received > ws->parser.length)
359
+ data_len = ws->parser.length - ws->parser.received;
367
360
 
368
- // a note about unmasking: since ws->parser.state.at_mask is only 2 bits,
369
- // it will wrap around (i.e. 3++ == 0), so no modulus is required :-)
370
- // unmask:
371
- if (ws->parser.sdata.masked) {
372
- for (int i = 0; i < data_len; i++) {
373
- read_buffer.buffer[i + read_buffer.pos] ^=
374
- ws->parser.mask[ws->parser.state.at_mask++];
375
- }
376
- } else if (ws->parser.client == 0) {
377
- // enforce masking unless acting as client, also for security reasons...
378
- fprintf(stderr, "ERROR Websockets: unmasked frame, disconnecting.\n");
379
- sock_close(sockfd);
380
- return;
361
+ // a note about unmasking: since ws->parser.state.at_mask is only 2 bits,
362
+ // it will wrap around (i.e. 3++ == 0), so no modulus is required :-)
363
+ // unmask:
364
+ if (ws->parser.sdata.masked) {
365
+ for (int i = 0; i < data_len; i++) {
366
+ read_buffer.buffer[i + read_buffer.pos] ^=
367
+ ws->parser.mask[ws->parser.state.at_mask++];
381
368
  }
382
- // Copy the data to the Websocket buffer - only if it's a user message
383
- if (data_len &&
384
- (ws->parser.head.op_code == 1 || ws->parser.head.op_code == 2 ||
385
- (!ws->parser.head.op_code && (ws->parser.head2.op_code == 1 ||
386
- ws->parser.head2.op_code == 2)))) {
387
- // review and resize the buffer's capacity - it can only grow.
388
- if (ws->length + ws->parser.length - ws->parser.received >
389
- ws->buffer.size) {
390
- ws->buffer = resize_ws_buffer(ws, ws->buffer);
391
- if (!ws->buffer.data) {
392
- // no memory.
393
- websocket_close(ws);
394
- return;
395
- }
396
- }
397
- // copy here
398
- memcpy((uint8_t *)ws->buffer.data + ws->length,
399
- read_buffer.buffer + read_buffer.pos, data_len);
400
- ws->length += data_len;
401
- }
402
- // set the frame's data received so far (copied or not)
403
- ws->parser.received += data_len;
404
-
405
- // check that we have collected the whole of the frame.
406
- if (ws->parser.length > ws->parser.received) {
407
- read_buffer.pos += data_len;
408
- // fprintf(stderr, "%p websocket has %lu out of %lu\n", (void *)ws,
409
- // ws->parser.received, ws->parser.length);
410
- continue;
411
- }
412
-
413
- // we have the whole frame, time to process the data.
414
- // pings, pongs and other non-user messages are handled independently.
415
- if (ws->parser.head.op_code == 0 || ws->parser.head.op_code == 1 ||
416
- ws->parser.head.op_code == 2) {
417
- /* a user data frame - make sure we got the `fin` flag, or an error
418
- * occured */
419
- if (!ws->parser.head.fin) {
420
- /* This frame was a partial message. */
421
- goto reset_state;
369
+ } else if (ws->parser.client == 0) {
370
+ // enforce masking unless acting as client, also for security reasons...
371
+ fprintf(stderr, "ERROR Websockets: unmasked frame, disconnecting.\n");
372
+ sock_close(sockfd);
373
+ return;
374
+ }
375
+ // Copy the data to the Websocket buffer - only if it's a user message
376
+ if (data_len &&
377
+ (ws->parser.head.op_code == 1 || ws->parser.head.op_code == 2 ||
378
+ (!ws->parser.head.op_code &&
379
+ (ws->parser.head2.op_code == 1 || ws->parser.head2.op_code == 2)))) {
380
+ // review and resize the buffer's capacity - it can only grow.
381
+ if (ws->length + ws->parser.length - ws->parser.received >
382
+ ws->buffer.size) {
383
+ ws->buffer = resize_ws_buffer(ws, ws->buffer);
384
+ if (!ws->buffer.data) {
385
+ // no memory.
386
+ websocket_close(ws);
387
+ return;
422
388
  }
423
- /* This was the last frame */
424
- if (ws->on_message) /* call the on_message callback */
425
- ws->on_message(ws, ws->buffer.data, ws->length,
426
- ws->parser.head2.op_code == 1);
427
- goto reset_parser;
428
- } else if (ws->parser.head.op_code == 8) {
429
- /* op-code == close */
430
- websocket_close(ws);
431
- if (ws->parser.head2.op_code == ws->parser.head.op_code)
432
- goto reset_parser;
433
- } else if (ws->parser.head.op_code == 9) {
434
- /* ping */
435
- // write Pong - including ping data...
436
- websocket_write_impl(sockfd, read_buffer.buffer + read_buffer.pos,
437
- data_len, 10, 1, 1, ws->parser.client);
438
- if (ws->parser.head2.op_code == ws->parser.head.op_code)
439
- goto reset_parser;
440
- } else if (ws->parser.head.op_code == 10) {
441
- /* pong */
442
- // do nothing... almost
443
- if (ws->parser.head2.op_code == ws->parser.head.op_code)
444
- goto reset_parser;
445
- } else if (ws->parser.head.op_code > 2 && ws->parser.head.op_code < 8) {
446
- /* future control frames. ignore. */
447
- if (ws->parser.head2.op_code == ws->parser.head.op_code)
448
- goto reset_parser;
449
- } else {
450
- /* WTF? */
451
- // fprintf(stderr, "%p websocket reached a WTF?! state..."
452
- // "op1: %i , op2: %i\n",
453
- // (void *)ws, ws->parser.head.op_code,
454
- // ws->parser.head2.op_code);
455
- fprintf(stderr, "ERROR Websockets: protocol error, disconnecting.\n");
456
- sock_close(sockfd);
457
- return;
458
389
  }
390
+ // copy here
391
+ memcpy((uint8_t *)ws->buffer.data + ws->length,
392
+ read_buffer.buffer + read_buffer.pos, data_len);
393
+ ws->length += data_len;
394
+ }
395
+ // set the frame's data received so far (copied or not)
396
+ ws->parser.received += data_len;
459
397
 
460
- reset_parser:
461
- ws->length = 0;
462
- // clear the parser's multi-frame state
463
- *((char *)(&(ws->parser.head2))) = 0;
464
- ws->parser.sdata.masked = 0;
465
- reset_state:
466
- // move the pos marker along - in case we have more then one frame in the
467
- // buffer
398
+ // check that we have collected the whole of the frame.
399
+ if (ws->parser.length > ws->parser.received) {
468
400
  read_buffer.pos += data_len;
469
- // reset parser state
470
- ws->parser.state.has_len = 0;
471
- ws->parser.state.at_len = 0;
472
- ws->parser.state.has_mask = 0;
473
- ws->parser.state.at_mask = 0;
474
- ws->parser.state.has_head = 0;
475
- ws->parser.sdata.size = 0;
476
- *((char *)(&(ws->parser.head))) = 0;
477
- ws->parser.received = ws->parser.length = ws->parser.psize.len2 =
478
- data_len = 0;
479
- if (read_buffer.pos < len) {
480
- /* we just finished an on_message callback, let's fragment the event. */
481
- ws->resume_from = len;
482
- facil_defer(.task = on_data_def, .task_type = FIO_PR_LOCK_TASK,
483
- .uuid = sockfd);
484
- return;
401
+ // fprintf(stderr, "%p websocket has %lu out of %lu\n", (void *)ws,
402
+ // ws->parser.received, ws->parser.length);
403
+ continue;
404
+ }
405
+
406
+ // we have the whole frame, time to process the data.
407
+ // pings, pongs and other non-user messages are handled independently.
408
+ if (ws->parser.head.op_code == 0 || ws->parser.head.op_code == 1 ||
409
+ ws->parser.head.op_code == 2) {
410
+ /* a user data frame - make sure we got the `fin` flag, or an error
411
+ * occured */
412
+ if (!ws->parser.head.fin) {
413
+ /* This frame was a partial message. */
414
+ goto reset_state;
485
415
  }
416
+ /* This was the last frame */
417
+ if (ws->on_message) /* call the on_message callback */
418
+ ws->on_message(ws, ws->buffer.data, ws->length,
419
+ ws->parser.head2.op_code == 1);
420
+ goto reset_parser;
421
+ } else if (ws->parser.head.op_code == 8) {
422
+ /* op-code == close */
423
+ websocket_close(ws);
424
+ if (ws->parser.head2.op_code == ws->parser.head.op_code)
425
+ goto reset_parser;
426
+ } else if (ws->parser.head.op_code == 9) {
427
+ /* ping */
428
+ // write Pong - including ping data...
429
+ websocket_write_impl(sockfd, read_buffer.buffer + read_buffer.pos,
430
+ data_len, 10, 1, 1, ws->parser.client);
431
+ if (ws->parser.head2.op_code == ws->parser.head.op_code)
432
+ goto reset_parser;
433
+ } else if (ws->parser.head.op_code == 10) {
434
+ /* pong */
435
+ // do nothing... almost
436
+ if (ws->parser.head2.op_code == ws->parser.head.op_code)
437
+ goto reset_parser;
438
+ } else if (ws->parser.head.op_code > 2 && ws->parser.head.op_code < 8) {
439
+ /* future control frames. ignore. */
440
+ if (ws->parser.head2.op_code == ws->parser.head.op_code)
441
+ goto reset_parser;
442
+ } else {
443
+ /* WTF? */
444
+ // fprintf(stderr, "%p websocket reached a WTF?! state..."
445
+ // "op1: %i , op2: %i\n",
446
+ // (void *)ws, ws->parser.head.op_code,
447
+ // ws->parser.head2.op_code);
448
+ fprintf(stderr, "ERROR Websockets: protocol error, disconnecting.\n");
449
+ sock_close(sockfd);
450
+ return;
486
451
  }
452
+
453
+ reset_parser:
454
+ ws->length = 0;
455
+ // clear the parser's multi-frame state
456
+ *((char *)(&(ws->parser.head2))) = 0;
457
+ ws->parser.sdata.masked = 0;
458
+ reset_state:
459
+ // move the pos marker along - in case we have more then one frame in the
460
+ // buffer
461
+ read_buffer.pos += data_len;
462
+ // reset parser state
463
+ ws->parser.state.has_len = 0;
464
+ ws->parser.state.at_len = 0;
465
+ ws->parser.state.has_mask = 0;
466
+ ws->parser.state.at_mask = 0;
467
+ ws->parser.state.has_head = 0;
468
+ ws->parser.sdata.size = 0;
469
+ *((char *)(&(ws->parser.head))) = 0;
470
+ ws->parser.received = ws->parser.length = ws->parser.psize.len2 = data_len =
471
+ 0;
487
472
  }
473
+ facil_force_event(sockfd, FIO_EVENT_ON_DATA);
488
474
  #undef ws
489
475
  }
490
476
 
@@ -1,3 +1,3 @@
1
1
  module Iodine
2
- VERSION = '0.4.2'.freeze
2
+ VERSION = '0.4.3'.freeze
3
3
  end
metadata CHANGED
@@ -1,7 +1,7 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: iodine
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.4.2
4
+ version: 0.4.3
5
5
  platform: ruby
6
6
  authors:
7
7
  - Boaz Segev