rage-iodine 2.2.0 → 3.0.0

Sign up to get free protection for your applications and to get access to all the features.
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