iodine 0.2.17 → 0.3.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 (55) hide show
  1. checksums.yaml +4 -4
  2. data/CHANGELOG.md +6 -0
  3. data/README.md +36 -3
  4. data/bin/config.ru +23 -2
  5. data/bin/http-hello +1 -1
  6. data/bin/ws-shootout +5 -0
  7. data/ext/iodine/defer.c +468 -0
  8. data/ext/iodine/defer.h +105 -0
  9. data/ext/iodine/evio.c +263 -0
  10. data/ext/iodine/evio.h +133 -0
  11. data/ext/iodine/extconf.rb +2 -1
  12. data/ext/iodine/facil.c +958 -0
  13. data/ext/iodine/facil.h +423 -0
  14. data/ext/iodine/http.c +90 -0
  15. data/ext/iodine/http.h +50 -12
  16. data/ext/iodine/http1.c +200 -267
  17. data/ext/iodine/http1.h +17 -26
  18. data/ext/iodine/http1_request.c +81 -0
  19. data/ext/iodine/http1_request.h +58 -0
  20. data/ext/iodine/http1_response.c +403 -0
  21. data/ext/iodine/http1_response.h +90 -0
  22. data/ext/iodine/http1_simple_parser.c +124 -108
  23. data/ext/iodine/http1_simple_parser.h +8 -3
  24. data/ext/iodine/http_request.c +104 -0
  25. data/ext/iodine/http_request.h +58 -102
  26. data/ext/iodine/http_response.c +212 -208
  27. data/ext/iodine/http_response.h +89 -252
  28. data/ext/iodine/iodine_core.c +57 -46
  29. data/ext/iodine/iodine_core.h +3 -1
  30. data/ext/iodine/iodine_http.c +105 -81
  31. data/ext/iodine/iodine_websocket.c +17 -13
  32. data/ext/iodine/iodine_websocket.h +1 -0
  33. data/ext/iodine/rb-call.c +9 -7
  34. data/ext/iodine/{rb-libasync.h → rb-defer.c} +57 -49
  35. data/ext/iodine/rb-rack-io.c +12 -6
  36. data/ext/iodine/rb-rack-io.h +1 -1
  37. data/ext/iodine/rb-registry.c +5 -2
  38. data/ext/iodine/sock.c +1159 -0
  39. data/ext/iodine/{libsock.h → sock.h} +138 -142
  40. data/ext/iodine/spnlock.inc +77 -0
  41. data/ext/iodine/websockets.c +101 -112
  42. data/ext/iodine/websockets.h +38 -19
  43. data/iodine.gemspec +3 -3
  44. data/lib/iodine/version.rb +1 -1
  45. data/lib/rack/handler/iodine.rb +6 -6
  46. metadata +23 -19
  47. data/ext/iodine/http_response_http1.h +0 -382
  48. data/ext/iodine/libasync.c +0 -570
  49. data/ext/iodine/libasync.h +0 -122
  50. data/ext/iodine/libreact.c +0 -350
  51. data/ext/iodine/libreact.h +0 -244
  52. data/ext/iodine/libserver.c +0 -957
  53. data/ext/iodine/libserver.h +0 -481
  54. data/ext/iodine/libsock.c +0 -1025
  55. data/ext/iodine/spnlock.h +0 -243
@@ -17,9 +17,11 @@ Feel free to copy, use and enjoy according to the license provided.
17
17
  #include <ruby/io.h>
18
18
  // clang-format on
19
19
 
20
- #include "libserver.h"
21
20
  #include "rb-call.h"
22
21
  #include "rb-registry.h"
22
+
23
+ #include "facil.h"
24
+
23
25
  #include <stdio.h>
24
26
  #include <stdlib.h>
25
27
  #include <string.h>
@@ -8,6 +8,9 @@ Feel free to copy, use and enjoy according to the license provided.
8
8
  #include "iodine_websocket.h"
9
9
  #include "websockets.h"
10
10
 
11
+ #include <arpa/inet.h>
12
+ #include <sys/socket.h>
13
+
11
14
  /* the Iodine::Rack HTTP server class*/
12
15
  VALUE IodineHttp;
13
16
  /* these three are used also by rb-rack-io.c */
@@ -38,6 +41,8 @@ static _Bool iodine_http_static_file_server = 0;
38
41
 
39
42
  #define rack_autoset(rack_name) rack_set((rack_name), #rack_name)
40
43
 
44
+ static uint8_t IODINE_IS_DEVELOPMENT_MODE = 0;
45
+
41
46
  static VALUE ENV_TEMPLATE;
42
47
 
43
48
  rack_declare(HTTP_SCHEME);
@@ -80,8 +85,9 @@ static inline VALUE copy2env(http_request_s *request) {
80
85
  env, REQUEST_METHOD,
81
86
  rb_enc_str_new(request->method, request->method_len, BinaryEncoding));
82
87
 
83
- rb_hash_aset(env, PATH_INFO, rb_enc_str_new(request->path, request->path_len,
84
- BinaryEncoding));
88
+ rb_hash_aset(
89
+ env, PATH_INFO,
90
+ rb_enc_str_new(request->path, request->path_len, BinaryEncoding));
85
91
  rb_hash_aset(
86
92
  env, QUERY_STRING,
87
93
  (request->query
@@ -98,10 +104,22 @@ static inline VALUE copy2env(http_request_s *request) {
98
104
  rb_hash_aset(env, SERVER_PROTOCOL, hname);
99
105
  rb_hash_aset(env, HTTP_VERSION, hname);
100
106
 
101
- // rack_declare(REMOTE_ADDR);
107
+ // Suppoer for Ruby web-console.
108
+ hname = rb_str_buf_new(64);
109
+ sock_peer_addr_s addrinfo = sock_peer_addr(request->fd);
110
+ if (addrinfo.addrlen &&
111
+ inet_ntop(
112
+ addrinfo.addr->sa_family,
113
+ addrinfo.addr->sa_family == AF_INET
114
+ ? (void *)&((struct sockaddr_in *)addrinfo.addr)->sin_addr
115
+ : (void *)&((struct sockaddr_in6 *)addrinfo.addr)->sin6_addr,
116
+ RSTRING_PTR(hname), 64)) {
117
+ rb_str_set_len(hname, strlen(RSTRING_PTR(hname)));
118
+ rb_hash_aset(env, REMOTE_ADDR, hname);
119
+ }
102
120
 
103
121
  /* setup input IO + hijack support */
104
- rb_hash_aset(env, R_INPUT, (hname = RackIO.new(request, env)));
122
+ rb_hash_aset(env, R_INPUT, (hname = RackIO.create(request, env)));
105
123
 
106
124
  /* publish upgrade support */
107
125
  if (request->upgrade) {
@@ -135,36 +153,37 @@ static inline VALUE copy2env(http_request_s *request) {
135
153
  rb_hash_aset(env, R_URL_SCHEME, HTTP_SCHEME);
136
154
 
137
155
  /* add all headers, exclude special cases */
138
- http_headers_s *header = request->headers;
139
- for (size_t i = 0; i < request->headers_count; i++, header++) {
140
- if (header->name_length == 14 &&
141
- strncasecmp("content-length", header->name, 14) == 0) {
156
+ http_header_s header = http_request_header_first(request);
157
+ while (header.name) {
158
+ if (header.name_len == 14 &&
159
+ strncasecmp("content-length", header.name, 14) == 0) {
142
160
  rb_hash_aset(
143
161
  env, CONTENT_LENGTH,
144
- rb_enc_str_new(header->value, header->value_length, BinaryEncoding));
162
+ rb_enc_str_new(header.value, header.value_len, BinaryEncoding));
163
+ header = http_request_header_next(request);
145
164
  continue;
146
- } else if (header->name_length == 12 &&
147
- strncasecmp("content-type", header->name, 12) == 0) {
165
+ } else if (header.name_len == 12 &&
166
+ strncasecmp("content-type", header.name, 12) == 0) {
148
167
  rb_hash_aset(
149
168
  env, CONTENT_TYPE,
150
- rb_enc_str_new(header->value, header->value_length, BinaryEncoding));
169
+ rb_enc_str_new(header.value, header.value_len, BinaryEncoding));
170
+ header = http_request_header_next(request);
151
171
  continue;
152
- } else if (header->name_length == 27 &&
153
- strncasecmp("x-forwarded-proto", header->name, 27) == 0) {
154
- if (header->value_length >= 5 &&
155
- !strncasecmp(header->value, "https", 5)) {
172
+ } else if (header.name_len == 27 &&
173
+ strncasecmp("x-forwarded-proto", header.name, 27) == 0) {
174
+ if (header.value_len >= 5 && !strncasecmp(header.value, "https", 5)) {
156
175
  rb_hash_aset(env, R_URL_SCHEME, HTTPS_SCHEME);
157
- } else if (header->value_length == 4 &&
158
- *((uint32_t *)header->value) == *((uint32_t *)"http")) {
176
+ } else if (header.value_len == 4 &&
177
+ *((uint32_t *)header.value) == *((uint32_t *)"http")) {
159
178
  rb_hash_aset(env, R_URL_SCHEME, HTTP_SCHEME);
160
179
  } else {
161
- rb_hash_aset(env, R_URL_SCHEME,
162
- rb_enc_str_new(header->value, header->value_length,
163
- BinaryEncoding));
180
+ rb_hash_aset(
181
+ env, R_URL_SCHEME,
182
+ rb_enc_str_new(header.value, header.value_len, BinaryEncoding));
164
183
  }
165
- } else if (header->name_length == 9 &&
166
- strncasecmp("forwarded", header->name, 9) == 0) {
167
- pos = (char *)header->value;
184
+ } else if (header.name_len == 9 &&
185
+ strncasecmp("forwarded", header.name, 9) == 0) {
186
+ pos = (char *)header.value;
168
187
  if (pos) {
169
188
  while (*pos) {
170
189
  if (((*(pos++) | 32) == 'p') && ((*(pos++) | 32) == 'r') &&
@@ -189,18 +208,20 @@ static inline VALUE copy2env(http_request_s *request) {
189
208
  }
190
209
  }
191
210
 
192
- hname = rb_str_buf_new(6 + header->name_length);
211
+ hname = rb_str_buf_new(6 + header.name_len);
193
212
  memcpy(RSTRING_PTR(hname), "HTTP_", 5);
194
213
  pos = RSTRING_PTR(hname) + 5;
195
- reader = header->name;
214
+ reader = header.name;
196
215
  while (*reader) {
197
216
  *(pos++) = *reader == '-' ? '_' : to_upper(*reader);
198
217
  ++reader;
199
218
  }
200
219
  *pos = 0;
201
- rb_str_set_len(hname, 5 + header->name_length);
202
- rb_hash_aset(env, hname, rb_enc_str_new(header->value, header->value_length,
203
- BinaryEncoding));
220
+ rb_str_set_len(hname, 5 + header.name_len);
221
+ rb_hash_aset(
222
+ env, hname,
223
+ rb_enc_str_new(header.value, header.value_len, BinaryEncoding));
224
+ header = http_request_header_next(request);
204
225
  }
205
226
  return env;
206
227
  }
@@ -227,8 +248,8 @@ static int for_each_header_data(VALUE key, VALUE val, VALUE _res) {
227
248
  while (pos_e < val_len && val_s[pos_e] != '\n')
228
249
  pos_e++;
229
250
  http_response_write_header(
230
- (void *)_res, .name = RSTRING_PTR(key), .name_length = RSTRING_LEN(key),
231
- .value = val_s + pos_s, .value_length = pos_e - pos_s);
251
+ (void *)_res, .name = RSTRING_PTR(key), .name_len = RSTRING_LEN(key),
252
+ .value = val_s + pos_s, .value_len = pos_e - pos_s);
232
253
  // fprintf(stderr, "For_each - headers: wrote header\n");
233
254
  // move forward (skip the '\n' if exists)
234
255
  pos_s = pos_e + 1;
@@ -258,7 +279,6 @@ static VALUE for_each_body_string(VALUE str, VALUE _res, int argc, VALUE argv) {
258
279
  return Qfalse;
259
280
  }
260
281
  } else {
261
- http_response_finish((void *)_res);
262
282
  return Qfalse;
263
283
  }
264
284
  return Qtrue;
@@ -285,24 +305,19 @@ static inline int ruby2c_response_send(http_response_s *response,
285
305
  // fprintf(stderr, "Review body as String\n");
286
306
  if (RSTRING_LEN(body))
287
307
  http_response_write_body(response, RSTRING_PTR(body), RSTRING_LEN(body));
288
- http_response_finish(response);
289
308
  return 0;
290
309
  } else if (body == Qnil) {
291
- http_response_finish(response);
292
310
  return 0;
293
311
  } else if (rb_respond_to(body, each_method_id)) {
294
312
  // fprintf(stderr, "Review body as for-each ...\n");
295
- if (!response->metadata.connection_written &&
296
- !response->metadata.content_length_written) {
313
+ if (!response->connection_written && !response->content_length_written) {
297
314
  // close the connection to indicate message length...
298
315
  // protection from bad code
299
- response->metadata.should_close = 1;
316
+ response->should_close = 1;
300
317
  response->content_length = -1;
301
318
  }
302
319
  rb_block_call(body, each_method_id, 0, NULL, for_each_body_string,
303
320
  (VALUE)response);
304
- // make sure the response is sent even if it was an empty collection
305
- http_response_finish(response);
306
321
  // we need to call `close` in case the object is an IO / BodyProxy
307
322
  if (rb_respond_to(body, close_method_id))
308
323
  RubyCaller.call(body, close_method_id);
@@ -318,24 +333,22 @@ static inline int ruby2c_review_upgrade(http_response_s *response,
318
333
  // send headers
319
334
  http_response_finish(response);
320
335
  // remove socket from libsock and libserver
321
- server_hijack(response->metadata.request->metadata.fd);
336
+ facil_attach(response->fd, NULL);
322
337
  // call the callback
323
338
  VALUE io_ruby = RubyCaller.call(rb_hash_aref(env, R_HIJACK), call_proc_id);
324
339
  RubyCaller.call2(handler, call_proc_id, 1, &io_ruby);
325
340
  } else if ((handler = rb_hash_aref(env, R_HIJACK_IO)) != Qnil) {
326
341
  // send nothing.
327
- if (iodine_http_request_logging)
328
- http_response_log_finish(response);
329
342
  http_response_destroy(response);
330
343
  // remove socket from libsock and libserver
331
- server_hijack(response->metadata.request->metadata.fd);
344
+ facil_attach(response->fd, NULL);
332
345
  } else if ((handler = rb_hash_aref(env, UPGRADE_WEBSOCKET)) != Qnil ||
333
346
  (handler = rb_hash_aref(env, IODINE_WEBSOCKET)) != Qnil) {
334
347
  // use response as existing base for native websocket upgrade
335
- iodine_websocket_upgrade(response->metadata.request, response, handler);
348
+ iodine_websocket_upgrade(response->request, response, handler);
336
349
  } else if ((handler = rb_hash_aref(env, UPGRADE_TCP)) != Qnil ||
337
350
  (handler = rb_hash_aref(env, IODINE_UPGRADE)) != Qnil) {
338
- intptr_t fduuid = response->metadata.request->metadata.fd;
351
+ intptr_t fduuid = response->fd;
339
352
  // send headers
340
353
  http_response_finish(response);
341
354
  // upgrade protocol
@@ -353,9 +366,9 @@ static inline int ruby2c_review_upgrade(http_response_s *response,
353
366
  }
354
367
 
355
368
  static void *on_rack_request_in_GVL(http_request_s *request) {
356
- http_response_s response = http_response_init(request);
369
+ http_response_s *response = http_response_create(request);
357
370
  if (iodine_http_request_logging)
358
- http_response_log_start(&response);
371
+ http_response_log_start(response);
359
372
  // create /register env variable
360
373
  VALUE env = copy2env(request);
361
374
  // will be used later
@@ -371,7 +384,7 @@ static void *on_rack_request_in_GVL(http_request_s *request) {
371
384
  tmp = rb_funcall2(tmp, to_fixnum_func_id, 0, NULL);
372
385
  if (TYPE(tmp) != T_FIXNUM)
373
386
  goto internal_error;
374
- response.status = FIX2ULONG(tmp);
387
+ response->status = FIX2ULONG(tmp);
375
388
  // handle header copy from ruby land to C land.
376
389
  VALUE response_headers = rb_ary_entry(rbresponse, 1);
377
390
  if (TYPE(response_headers) != T_HASH)
@@ -386,35 +399,37 @@ static void *on_rack_request_in_GVL(http_request_s *request) {
386
399
  rb_hash_delete(response_headers, CONTENT_LENGTH_HEADER);
387
400
  }
388
401
  // review each header and write it to the response.
389
- rb_hash_foreach(response_headers, for_each_header_data, (VALUE)(&response));
402
+ rb_hash_foreach(response_headers, for_each_header_data, (VALUE)(response));
390
403
  // If the X-Sendfile header was provided, send the file directly and finish
391
404
  if (xfiles != Qnil &&
392
- http_response_sendfile2(&response, request, RSTRING_PTR(xfiles),
393
- RSTRING_LEN(xfiles), NULL, 0, 1) == 0) {
394
- goto finish;
395
- }
405
+ http_response_sendfile2(response, request, RSTRING_PTR(xfiles),
406
+ RSTRING_LEN(xfiles), NULL, 0, 1) == 0)
407
+ goto external_done;
396
408
  // review for belated (post response headers) upgrade.
397
- if (ruby2c_review_upgrade(&response, rbresponse, env) == 0) {
398
- // send the request body.
399
- if (ruby2c_response_send(&response, rbresponse, env))
400
- goto internal_error;
401
- // http_response_finish(&response);
402
- }
403
- finish:
409
+ if (ruby2c_review_upgrade(response, rbresponse, env))
410
+ goto external_done;
411
+ // send the request body.
412
+ if (ruby2c_response_send(response, rbresponse, env))
413
+ goto internal_error;
414
+
415
+ Registry.remove(rbresponse);
416
+ Registry.remove(env);
417
+ http_response_finish(response);
418
+ return NULL;
419
+ external_done:
404
420
  Registry.remove(rbresponse);
405
421
  Registry.remove(env);
406
- http_response_destroy(&response);
407
422
  return NULL;
408
423
  internal_error:
409
424
  Registry.remove(rbresponse);
410
425
  Registry.remove(env);
411
- http_response_destroy(&response);
412
- response = http_response_init(request);
426
+ http_response_destroy(response);
427
+ response = http_response_create(request);
413
428
  if (iodine_http_request_logging)
414
- http_response_log_start(&response);
415
- response.status = 500;
416
- http_response_write_body(&response, "Error 500, Internal error.", 26);
417
- http_response_finish(&response);
429
+ http_response_log_start(response);
430
+ response->status = 500;
431
+ http_response_write_body(response, "Error 500, Internal error.", 26);
432
+ http_response_finish(response);
418
433
  return NULL;
419
434
  }
420
435
 
@@ -484,6 +499,10 @@ Rack object API
484
499
  */
485
500
 
486
501
  int iodine_http_review(void) {
502
+ if ((getenv("RACK_ENV") && !strcasecmp(getenv("RACK_ENV"), "development")) ||
503
+ (getenv("RAILS_ENV") && !strcasecmp(getenv("RAILS_ENV"), "development")))
504
+ IODINE_IS_DEVELOPMENT_MODE = 1;
505
+
487
506
  rack_app_handler = rb_iv_get(IodineHttp, "@app");
488
507
  if (rack_app_handler != Qnil &&
489
508
  rb_respond_to(rack_app_handler, call_proc_id)) {
@@ -504,8 +523,11 @@ int iodine_http_review(void) {
504
523
  TYPE(rbport) != Qnil)
505
524
  rb_raise(rb_eTypeError,
506
525
  "The port variable must be either a Fixnum or a String.");
507
- if (TYPE(rbport) == T_FIXNUM)
526
+ if (TYPE(rbport) == T_FIXNUM) {
508
527
  rbport = rb_funcall2(rbport, rb_intern("to_s"), 0, NULL);
528
+ // rb_ivar_set(self, rb_intern("_port"), port);
529
+ rb_iv_set(IodineHttp, "@port", rbport);
530
+ }
509
531
  if (TYPE(rbport) == T_STRING)
510
532
  port = StringValueCStr(rbport);
511
533
  // review address
@@ -554,26 +576,28 @@ int iodine_http_review(void) {
554
576
  VALUE iodine_version = rb_const_get(Iodine, rb_intern("VERSION"));
555
577
  VALUE ruby_version = rb_const_get(Iodine, rb_intern("RUBY_VERSION"));
556
578
  if (public_folder)
557
- fprintf(stderr, "Starting up Iodine HTTP Server:\n"
558
- " * Ruby v.%s\n * Iodine v.%s \n"
559
- " * %lu max concurrent connections / open files\n"
560
- " * Serving static files from:\n"
561
- " %s\n\n",
579
+ fprintf(stderr,
580
+ "Starting up Iodine HTTP Server:\n"
581
+ " * Ruby v.%s\n * Iodine v.%s \n"
582
+ " * %lu max concurrent connections / open files\n"
583
+ " * Serving static files from:\n"
584
+ " %s\n\n",
562
585
  StringValueCStr(ruby_version), StringValueCStr(iodine_version),
563
586
  (size_t)sock_max_capacity(), public_folder);
564
587
  else
565
- fprintf(stderr, "Starting up Iodine HTTP Server:\n"
566
- " * Ruby v.%s\n * Iodine v.%s \n"
567
- " * %lu max concurrent connections / open files\n"
568
- "\n",
588
+ fprintf(stderr,
589
+ "Starting up Iodine HTTP Server:\n"
590
+ " * Ruby v.%s\n * Iodine v.%s \n"
591
+ " * %lu max concurrent connections / open files\n"
592
+ "\n",
569
593
  StringValueCStr(ruby_version), StringValueCStr(iodine_version),
570
594
  (size_t)sock_max_capacity());
571
595
 
572
596
  // listen
573
- return http1_listen(port, address, .on_request = on_rack_request,
574
- .log_static = iodine_http_request_logging,
575
- .max_body_size = max_body_size,
576
- .public_folder = public_folder, .timeout = timeout);
597
+ return http_listen(port, address, .on_request = on_rack_request,
598
+ .log_static = iodine_http_request_logging,
599
+ .max_body_size = max_body_size,
600
+ .public_folder = public_folder, .timeout = timeout);
577
601
  }
578
602
  return 0;
579
603
  }
@@ -25,7 +25,7 @@ size_t iodine_websocket_max_msg_size = 0;
25
25
  uint8_t iodine_websocket_timeout = 0;
26
26
 
27
27
  #define set_uuid(object, request) \
28
- rb_ivar_set((object), fd_var_id, ULONG2NUM((request)->metadata.fd))
28
+ rb_ivar_set((object), fd_var_id, ULONG2NUM((request)->fd))
29
29
 
30
30
  inline static intptr_t get_uuid(VALUE obj) {
31
31
  VALUE i = rb_ivar_get(obj, fd_var_id);
@@ -42,8 +42,8 @@ inline static ws_s *get_ws(VALUE obj) {
42
42
  return (ws_s *)FIX2ULONG(i);
43
43
  }
44
44
 
45
- #define set_handler(ws, handler) websocket_set_udata((ws), (VALUE)handler)
46
- #define get_handler(ws) ((VALUE)websocket_get_udata((ws_s *)(ws)))
45
+ #define set_handler(ws, handler) websocket_udata_set((ws), (VALUE)handler)
46
+ #define get_handler(ws) ((VALUE)websocket_udata((ws_s *)(ws)))
47
47
 
48
48
  /*******************************************************************************
49
49
  Buffer management - update to change the way the buffer is handled.
@@ -154,8 +154,8 @@ static VALUE iodine_ws_write(VALUE self, VALUE data) {
154
154
  /** Returns the number of active websocket connections (including connections
155
155
  * that are in the process of closing down). */
156
156
  static VALUE iodine_ws_count(VALUE self) {
157
- ws_s *ws = get_ws(self);
158
- return LONG2FIX(websocket_count(ws));
157
+ return LONG2FIX(websocket_count());
158
+ (void)self;
159
159
  }
160
160
 
161
161
  /**
@@ -165,7 +165,7 @@ return `true`, otherwise `false` will be returned.
165
165
  */
166
166
  static VALUE iodine_ws_has_pending(VALUE self) {
167
167
  intptr_t uuid = get_uuid(self);
168
- return sock_packets_pending(uuid) ? Qtrue : Qfalse;
168
+ return sock_has_pending(uuid) ? Qtrue : Qfalse;
169
169
  }
170
170
 
171
171
  /**
@@ -236,7 +236,8 @@ static VALUE iodine_defer(int argc, VALUE *argv, VALUE self) {
236
236
  return Qfalse;
237
237
  Registry.add(block);
238
238
 
239
- server_task(fd, iodine_perform_defer, (void *)block, iodine_defer_fallback);
239
+ facil_defer(.uuid = fd, .task = iodine_perform_defer, .arg = (void *)block,
240
+ .fallback = iodine_defer_fallback);
240
241
  return block;
241
242
  }
242
243
 
@@ -245,6 +246,8 @@ Websocket Multi-Write
245
246
  */
246
247
 
247
248
  static uint8_t iodine_ws_if_callback(ws_s *ws, void *block) {
249
+ if (!ws)
250
+ return 0;
248
251
  VALUE handler = get_handler(ws);
249
252
  uint8_t ret = 0;
250
253
  if (handler)
@@ -305,16 +308,15 @@ static void iodine_ws_perform_each_task(intptr_t fd, protocol_s *protocol,
305
308
  if (handler)
306
309
  RubyCaller.call2((VALUE)data, call_proc_id, 1, &handler);
307
310
  }
308
- static void iodine_ws_finish_each_task(intptr_t fd, protocol_s *protocol,
309
- void *data) {
311
+ static void iodine_ws_finish_each_task(intptr_t fd, void *data) {
310
312
  (void)(fd);
311
- (void)(protocol);
312
313
  Registry.remove((VALUE)data);
313
314
  }
314
315
 
315
316
  inline static void iodine_ws_run_each(intptr_t origin, VALUE block) {
316
- server_each(origin, WEBSOCKET_ID_STR, iodine_ws_perform_each_task,
317
- (void *)block, iodine_ws_finish_each_task);
317
+ facil_each(.origin = origin, .service = WEBSOCKET_ID_STR,
318
+ .task = iodine_ws_perform_each_task, .arg = (void *)block,
319
+ .on_complete = iodine_ws_finish_each_task);
318
320
  }
319
321
 
320
322
  /** Performs a block of code for each websocket connection. The function returns
@@ -395,7 +397,8 @@ static VALUE iodine_class_defer(VALUE self, VALUE ws_uuid) {
395
397
  return Qfalse;
396
398
  Registry.add(block);
397
399
 
398
- server_task(fd, iodine_perform_defer, (void *)block, iodine_defer_fallback);
400
+ facil_defer(.uuid = fd, .task = iodine_perform_defer, .arg = (void *)block,
401
+ .fallback = iodine_defer_fallback);
399
402
  return block;
400
403
  }
401
404
 
@@ -413,6 +416,7 @@ void ws_on_close(ws_s *ws) {
413
416
  if (!handler)
414
417
  return;
415
418
  RubyCaller.call(handler, on_close_func_id);
419
+ set_ws(handler, Qnil);
416
420
  Registry.remove(handler);
417
421
  }
418
422
  void ws_on_shutdown(ws_s *ws) {