rage-iodine 2.2.0 → 3.0.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: 26c64867b568efaa1a0ae783f20270a2b5ed8c0fcc010092618c5d8dbc44c90b
4
- data.tar.gz: 626631091ff1089858442160f6ef8e37acbed44dfa31f4840c8687ef6fa127c0
3
+ metadata.gz: 15eb3d0d5e2834bc02a3e116dd07cd2fa36a73ad56ff7812579f1406a6d2874a
4
+ data.tar.gz: 3d9c061fe439e73a262a3c6ce0cd07f56d3ba2226b309caec539e202701e2249
5
5
  SHA512:
6
- metadata.gz: 46a415053aeb26048f786b2231c303e0d420dd01c25e0bb549e5b982952254fef69939a9f5771cdf8a6a4b54604496ac78853dedfae21a7577481f6e2f242654
7
- data.tar.gz: 06f1410986a09fa0d5316907d02aeab6875dd2d7887ae679c3b289e0bb6222c23b208ad2e221dd425f2991dd9f4dd358f8ed68aaea36fb6852fb58a97dc42a15
6
+ metadata.gz: 2b8d6ed6f9372e1cbfdd089de2e6d9941e2ef20da98ac5f47e603bcd5369ea4afb51b89fb7e80e01869f4752a8679bcf094d60f3a29930641c98fabe2c3481d9
7
+ data.tar.gz: 39f880d839140587dde14e5e1fa3b68bdbb0e35db97333de28a0d024d9d3c8b18cb3575098baeaa813cfc8eb03b459e3042bf6a70aeeec5f57b60557eafddb38
data/CHANGELOG.md CHANGED
@@ -6,6 +6,42 @@ Please notice that this change log contains changes for upcoming releases as wel
6
6
 
7
7
  ## Changes:
8
8
 
9
+ #### Change log v.3.0.0 (2023-12-09)
10
+
11
+ **Update**: Schedule request fibers.
12
+
13
+ #### Change log v.2.3.0 (2023-11-27)
14
+
15
+ **Fix**: Correctly handle non-persistent HTTP connections.
16
+
17
+ #### Change log v.2.2.0 (2023-11-20)
18
+
19
+ **Update**: Add `gen_request_tag` and `gen_timestamp` methods.
20
+
21
+ **Fix**: Use separate buffer for writing.
22
+
23
+ #### Change log v.2.1.1 (2023-11-09)
24
+
25
+ **Fix**: Correctly update the fulfilled flag.
26
+
27
+ **Fix**: Always send a response.
28
+
29
+ #### Change log v.2.1.0 (2023-11-03)
30
+
31
+ **Fix**: Return an error on timeouts.
32
+
33
+ #### Change log v.2.0.0 (2023-10-31)
34
+
35
+ **Update**: Parse params.
36
+
37
+ #### Change log v.1.8.0 (2023-09-25)
38
+
39
+ **Fix**: Free up memory when closing the protocol.
40
+
41
+ #### Change log v.1.7.58 (2023-09-15)
42
+
43
+ **Update**: Allow to pause and resume fiber requests.
44
+
9
45
  #### Change log v.0.7.57 (2023-09-04)
10
46
 
11
47
  **Fix**: Fixes possible name collision when loading gem (`.rb` vs. `.so` loading). Credit to @noraj (Alexandre ZANNI) for opening issue #148. Credit to @janbiedermann (Jan Biedermann) for discovering the root cause and offering a solution.
data/Gemfile CHANGED
@@ -5,6 +5,7 @@ group :test do
5
5
  gem 'rspec'
6
6
  gem 'rack'
7
7
  gem 'http'
8
+ gem "rage-rb"
8
9
  end
9
10
 
10
11
  # Specify your gem's dependencies in iodine.gemspec
data/ext/iodine/fio.c CHANGED
@@ -2051,7 +2051,7 @@ static size_t fio_poll(void) {
2051
2051
  epoll_wait(internal[j].data.fd, events, FIO_POLL_MAX_EVENTS, 0);
2052
2052
  if (active_count > 0) {
2053
2053
  for (int i = 0; i < active_count; i++) {
2054
- if (events[i].events & (~(EPOLLIN | EPOLLOUT))) {
2054
+ if (events[i].events & (~(EPOLLIN | EPOLLOUT | EPOLLHUP | EPOLLRDHUP))) {
2055
2055
  // errors are hendled as disconnections (on_close)
2056
2056
  fio_force_close_in_poll(fd2uuid(events[i].data.fd));
2057
2057
  } else {
@@ -2060,9 +2060,14 @@ static size_t fio_poll(void) {
2060
2060
  fio_defer_push_urgent(deferred_on_ready,
2061
2061
  (void *)fd2uuid(events[i].data.fd), NULL);
2062
2062
  }
2063
- if (events[i].events & EPOLLIN)
2063
+ if (events[i].events & EPOLLIN) {
2064
2064
  fio_defer_push_task(deferred_on_data,
2065
2065
  (void *)fd2uuid(events[i].data.fd), NULL);
2066
+ }
2067
+ if (events[i].events & (EPOLLHUP | EPOLLRDHUP)) {
2068
+ fio_defer_push_task(fio_force_close_in_poll,
2069
+ (void *)fd2uuid(events[i].data.fd), NULL);
2070
+ }
2066
2071
  }
2067
2072
  } // end for loop
2068
2073
  total += active_count;
@@ -2197,7 +2202,8 @@ static size_t fio_poll(void) {
2197
2202
  NULL);
2198
2203
  }
2199
2204
  if (events[i].flags & (EV_EOF | EV_ERROR)) {
2200
- fio_force_close_in_poll(fd2uuid(events[i].udata));
2205
+ fio_defer_push_task(fio_force_close_in_poll,
2206
+ (void *)fd2uuid(events[i].udata), NULL);
2201
2207
  }
2202
2208
  }
2203
2209
  } else if (active_count < 0) {
data/ext/iodine/http.c CHANGED
@@ -737,6 +737,8 @@ static void http_resume_wrapper(intptr_t uuid, fio_protocol_s *p_, void *arg) {
737
737
  http_pause_handle_s *http = arg;
738
738
  http_s *h = http->h;
739
739
  h->udata = http->udata;
740
+ h->fiber = http->fiber;
741
+ h->subscription = http->subscription;
740
742
  http_vtable_s *vtbl = (http_vtable_s *)h->private_data.vtbl;
741
743
  if (http->task)
742
744
  http->task(h);
@@ -769,6 +771,8 @@ void http_pause(http_s *h, void (*task)(http_pause_handle_s *http)) {
769
771
  .uuid = p->uuid,
770
772
  .h = h,
771
773
  .udata = h->udata,
774
+ .fiber = h->fiber,
775
+ .subscription = h->subscription,
772
776
  };
773
777
  vtbl->http_on_pause(h, p);
774
778
  fio_defer(http_pause_wrapper, http, (void *)((uintptr_t)task));
data/ext/iodine/http.h CHANGED
@@ -128,11 +128,8 @@ typedef struct {
128
128
  /** in case the request was paused, this will hold a Ruby fiber, that was scheduled during the request. */
129
129
  void *fiber;
130
130
 
131
- /**
132
- * in case the request needs to be paused, Iodine will subscribe to a channel with this name;
133
- * once Ruby finishes processing, it will publish to this channel telling Iodine the request can be resumed.
134
- */
135
- FIOBJ request_id;
131
+ /** in case the request needs to be paused, Iodine will subscribe to a channel identified by this object. */
132
+ void *subscription;
136
133
  } http_s;
137
134
 
138
135
  /**
@@ -283,6 +280,8 @@ struct http_pause_handle_s {
283
280
  uintptr_t uuid;
284
281
  http_s *h;
285
282
  void *udata;
283
+ void *fiber;
284
+ void *subscription;
286
285
  void (*task)(http_s *);
287
286
  void (*fallback)(void *);
288
287
  };
@@ -97,10 +97,6 @@ HTTP request/response object management
97
97
 
98
98
  static inline void http_s_new(http_s *h, http_fio_protocol_s *owner,
99
99
  http_vtable_s *vtbl) {
100
- FIOBJ request_id = fiobj_str_buf(14);
101
- fiobj_str_write(request_id, "req:", 4);
102
- fiobj_str_write_i(request_id, (uint32_t)fio_rand64());
103
-
104
100
  *h = (http_s){
105
101
  .private_data =
106
102
  {
@@ -110,8 +106,7 @@ static inline void http_s_new(http_s *h, http_fio_protocol_s *owner,
110
106
  },
111
107
  .headers = fiobj_hash_new(),
112
108
  .received_at = fio_last_tick(),
113
- .status = 200,
114
- .request_id = request_id,
109
+ .status = 200
115
110
  };
116
111
  }
117
112
 
@@ -129,9 +124,9 @@ static inline void http_s_destroy(http_s *h, uint8_t log) {
129
124
  fiobj_free(h->cookies);
130
125
  fiobj_free(h->body);
131
126
  fiobj_free(h->params);
132
- fiobj_free(h->request_id);
133
127
 
134
- IodineStore.remove((VALUE)h->fiber);
128
+ h->fiber = NULL;
129
+ h->subscription = NULL;
135
130
 
136
131
  *h = (http_s){
137
132
  .private_data.vtbl = h->private_data.vtbl,
@@ -60,7 +60,9 @@ static ID each_method_id;
60
60
  static ID attach_method_id;
61
61
  static ID iodine_call_proc_id;
62
62
  static ID fiber_result_var_id;
63
- static VALUE http_wait_directive;
63
+ static ID fiber_id_method_id;
64
+ static VALUE rb_cFiber;
65
+ static ID schedule_method_id;
64
66
 
65
67
  static VALUE env_template_no_upgrade;
66
68
  static VALUE env_template_websockets;
@@ -118,7 +120,7 @@ typedef struct {
118
120
  IODINE_HTTP_XSENDFILE,
119
121
  IODINE_HTTP_EMPTY,
120
122
  IODINE_HTTP_ERROR,
121
- IODINE_HTTP_WAIT,
123
+ IODINE_HTTP_DEFERRED,
122
124
  } type;
123
125
  enum iodine_upgrade_type_enum {
124
126
  IODINE_UPGRADE_NONE = 0,
@@ -470,9 +472,6 @@ static inline VALUE copy2env(iodine_http_request_handle_s *handle) {
470
472
  /* no TLS, no forwarding, assume `http`, which is the default */
471
473
  }
472
474
  }
473
- {
474
- rb_hash_aset(env, IODINE_REQUEST_ID, rb_str_new_cstr(fiobj_obj2cstr(handle->h->request_id).data));
475
- }
476
475
 
477
476
  /* add all remaining headers */
478
477
  fiobj_each1(h->headers, 0, iodine_copy2env_task, (void *)env);
@@ -674,33 +673,36 @@ Handling HTTP requests
674
673
  static inline void *iodine_handle_request_in_GVL(void *handle_) {
675
674
  iodine_http_request_handle_s *handle = handle_;
676
675
  VALUE rbresponse = 0;
677
- VALUE env = 0;
676
+ VALUE env = 0, tmp;
678
677
  http_s *h = handle->h;
679
678
  if (!h->udata)
680
679
  goto err_not_found;
681
680
 
682
- // create / register env variable
683
- env = copy2env(handle);
684
- // create rack.io
685
- VALUE tmp = IodineRackIO.create(h, env);
686
- // pass env variable to handler
687
- rbresponse =
688
- IodineCaller.call2((VALUE)h->udata, iodine_call_proc_id, 1, &env);
689
- // close rack.io
690
- IodineRackIO.close(tmp);
681
+ if (handle->type == IODINE_HTTP_DEFERRED) {
682
+ // the deferred response is now ready so fetch it from the fiber
683
+ rbresponse = rb_ivar_get((VALUE)h->fiber, fiber_result_var_id);
684
+ } else {
685
+ // create / register env variable
686
+ env = copy2env(handle);
687
+ // create rack.io
688
+ tmp = IodineRackIO.create(h, env);
689
+ // pass env variable to handler
690
+ rbresponse = IodineCaller.call2((VALUE)h->udata, iodine_call_proc_id, 1, &env);
691
+ IodineStore.add(rbresponse);
692
+ // close rack.io
693
+ IodineRackIO.close(tmp);
694
+
695
+ // if there's a fiber in the http_s object means the request has to be deferred
696
+ if (h->fiber)
697
+ goto defer;
698
+ }
699
+
691
700
  // test handler's return value
692
701
  if (rbresponse == 0 || rbresponse == Qnil || TYPE(rbresponse) != T_ARRAY) {
693
702
  goto internal_error;
694
703
  }
695
704
 
696
705
  tmp = rb_ary_entry(rbresponse, 0);
697
- // rack will return `[:__http_defer__, fiber_to_wait_on]` in case the request needs to be paused
698
- if (TYPE(tmp) == T_SYMBOL && tmp == http_wait_directive) {
699
- h->fiber = (void *)IodineStore.add(rb_ary_entry(rbresponse, 1));
700
- goto defer;
701
- }
702
-
703
- IodineStore.add(rbresponse);
704
706
  // set response status
705
707
  if (TYPE(tmp) == T_STRING) {
706
708
  char *data = RSTRING_PTR(tmp);
@@ -739,8 +741,7 @@ static inline void *iodine_handle_request_in_GVL(void *handle_) {
739
741
  // review each header and write it to the response.
740
742
  rb_hash_foreach(response_headers, for_each_header_data, (VALUE)(h));
741
743
  // review for upgrade.
742
- if ((intptr_t)h->status < 300 &&
743
- ruby2c_review_upgrade(handle, rbresponse, env))
744
+ if (handle->type != IODINE_HTTP_DEFERRED && (intptr_t)h->status < 300 && ruby2c_review_upgrade(handle, rbresponse, env))
744
745
  goto external_done;
745
746
  // send the request body.
746
747
  if (ruby2c_response_send(handle, rbresponse, env))
@@ -749,68 +750,31 @@ static inline void *iodine_handle_request_in_GVL(void *handle_) {
749
750
  finish:
750
751
  IodineStore.remove(rbresponse);
751
752
  IodineStore.remove(env);
752
- return NULL;
753
+ return (void *)rbresponse;
753
754
 
754
755
  external_done:
755
756
  IodineStore.remove(rbresponse);
756
757
  IodineStore.remove(env);
757
758
  handle->type = IODINE_HTTP_NONE;
758
- return NULL;
759
+ return (void *)rbresponse;
759
760
 
760
761
  err_not_found:
761
762
  IodineStore.remove(rbresponse);
762
763
  IodineStore.remove(env);
763
764
  h->status = 404;
764
765
  handle->type = IODINE_HTTP_ERROR;
765
- return NULL;
766
+ return (void *)rbresponse;
766
767
 
767
768
  internal_error:
768
769
  IodineStore.remove(rbresponse);
769
770
  IodineStore.remove(env);
770
771
  h->status = 500;
771
772
  handle->type = IODINE_HTTP_ERROR;
772
- return NULL;
773
+ return (void *)rbresponse;
773
774
 
774
775
  defer:
775
776
  IodineStore.remove(env);
776
- handle->type = IODINE_HTTP_WAIT;
777
- return NULL;
778
- }
779
-
780
- // called once a request that was paused previously needs to be resumed
781
- static inline void *iodine_handle_deferred_request_in_GVL(void *handle_) {
782
- iodine_http_request_handle_s *handle = handle_;
783
- http_s *h = handle->h;
784
-
785
- VALUE rbresponse = rb_ivar_get((VALUE)h->fiber, fiber_result_var_id);
786
- VALUE tmp = rb_ary_entry(rbresponse, 0);
787
-
788
- // set response status
789
- if (TYPE(tmp) == T_STRING) {
790
- char *data = RSTRING_PTR(tmp);
791
- h->status = fio_atol(&data);
792
- } else if (TYPE(tmp) == T_FIXNUM) {
793
- h->status = FIX2ULONG(tmp);
794
- } else {
795
- goto internal_error;
796
- }
797
-
798
- // handle header copy from ruby land to C land.
799
- VALUE response_headers = rb_ary_entry(rbresponse, 1);
800
-
801
- // review each header and write it to the response.
802
- rb_hash_foreach(response_headers, for_each_header_data, (VALUE)(h));
803
-
804
- // send the request body.
805
- if (ruby2c_response_send(handle, rbresponse, 0))
806
- goto internal_error;
807
-
808
- return NULL;
809
-
810
- internal_error:
811
- h->status = 500;
812
- handle->type = IODINE_HTTP_ERROR;
813
- return NULL;
777
+ return (void *)rbresponse;
814
778
  }
815
779
 
816
780
  static inline void
@@ -856,10 +820,13 @@ static inline void http_resume_deferred_request_handler(http_s *h) {
856
820
  iodine_http_request_handle_s handle = (iodine_http_request_handle_s){
857
821
  .h = h,
858
822
  .upgrade = IODINE_UPGRADE_NONE,
823
+ .type = IODINE_HTTP_DEFERRED,
859
824
  };
860
825
 
861
- IodineCaller.enterGVL((void *(*)(void *))iodine_handle_deferred_request_in_GVL,
862
- &handle);
826
+ IodineCaller.enterGVL((void *(*)(void *))iodine_handle_request_in_GVL, &handle);
827
+
828
+ IodineStore.remove((VALUE)h->fiber);
829
+ fio_unsubscribe((subscription_s *)h->subscription);
863
830
 
864
831
  iodine_perform_handle_action(handle);
865
832
  }
@@ -868,17 +835,26 @@ static inline void on_iodine_request_id_message(fio_msg_s *msg) {
868
835
  http_resume((http_pause_handle_s *)msg->udata1, http_resume_deferred_request_handler, NULL);
869
836
  }
870
837
 
871
- static inline void http_close_deferred_request_handler(void *sub) {
872
- fio_unsubscribe((subscription_s *)sub);
873
- }
874
-
875
- // when Ruby sends a message into the `request_id` channel means the fiber attached to
876
- // to the `http_s h` var can be resumed
877
838
  static inline void http_pause_request_handler(http_pause_handle_s *s) {
878
- subscription_s *sub = fio_subscribe(.channel = fiobj_obj2cstr(s->h->request_id),
839
+ VALUE fiber_id = rb_funcall((VALUE)s->fiber, fiber_id_method_id, 0);
840
+
841
+ subscription_s *sub = fio_subscribe(.channel = {0, RSTRING_LEN(fiber_id), RSTRING_PTR(fiber_id)},
879
842
  .on_message = on_iodine_request_id_message,
880
843
  .udata1 = (void *)s);
881
- fio_uuid_link(s->uuid, (void *)sub, http_close_deferred_request_handler);
844
+
845
+ s->subscription = (void *)sub;
846
+ }
847
+
848
+ static VALUE fiber_request_block(RB_BLOCK_CALL_FUNC_ARGLIST(_, handle_)) {
849
+ VALUE fiber_id = rb_funcall(rb_fiber_current(), fiber_id_method_id, 0);
850
+
851
+ iodine_http_request_handle_s *handle = (iodine_http_request_handle_s *)handle_;
852
+ VALUE rbresponse = (VALUE)IodineCaller.enterGVL((void *(*)(void *))iodine_handle_request_in_GVL, handle);
853
+
854
+ fio_publish(.channel = {0, RSTRING_LEN(fiber_id), RSTRING_PTR(fiber_id)});
855
+ rb_ivar_set(rb_fiber_current(), fiber_result_var_id, rbresponse);
856
+
857
+ return rbresponse;
882
858
  }
883
859
 
884
860
  static void on_rack_request(http_s *h) {
@@ -886,11 +862,14 @@ static void on_rack_request(http_s *h) {
886
862
  .h = h,
887
863
  .upgrade = IODINE_UPGRADE_NONE,
888
864
  };
889
- IodineCaller.enterGVL((void *(*)(void *))iodine_handle_request_in_GVL,
890
- &handle);
891
865
 
892
- if (handle.type == IODINE_HTTP_WAIT) {
893
- http_pause(handle.h, http_pause_request_handler);
866
+ VALUE fiber = rb_block_call(rb_cFiber, schedule_method_id, 0, NULL, fiber_request_block, (VALUE)&handle);
867
+
868
+ // the fiber encountered blocking IO and yielded - pause the request
869
+ if (rb_fiber_alive_p(fiber)) {
870
+ IodineStore.add(fiber);
871
+ h->fiber = (void *)fiber;
872
+ http_pause(h, http_pause_request_handler);
894
873
  } else {
895
874
  iodine_perform_handle_action(handle);
896
875
  }
@@ -1270,7 +1249,9 @@ void iodine_init_http(void) {
1270
1249
  attach_method_id = rb_intern("attach_fd");
1271
1250
  iodine_call_proc_id = rb_intern("call");
1272
1251
  fiber_result_var_id = rb_intern("@__result");
1273
- http_wait_directive = ID2SYM(rb_intern("__http_defer__"));
1252
+ fiber_id_method_id = rb_intern("__get_id");
1253
+ rb_cFiber = rb_const_get(rb_cObject, rb_intern("Fiber"));
1254
+ schedule_method_id = rb_intern("schedule");
1274
1255
 
1275
1256
  IodineUTF8Encoding = rb_enc_find("UTF-8");
1276
1257
  IodineBinaryEncoding = rb_enc_find("binary");
@@ -1,3 +1,3 @@
1
1
  module Iodine
2
- VERSION = '2.2.0'.freeze
2
+ VERSION = '3.0.0'.freeze
3
3
  end
metadata CHANGED
@@ -1,14 +1,14 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: rage-iodine
3
3
  version: !ruby/object:Gem::Version
4
- version: 2.2.0
4
+ version: 3.0.0
5
5
  platform: ruby
6
6
  authors:
7
7
  - Boaz Segev
8
8
  autorequire:
9
9
  bindir: exe
10
10
  cert_chain: []
11
- date: 2023-11-20 00:00:00.000000000 Z
11
+ date: 2023-12-09 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: rake