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 +4 -4
- data/CHANGELOG.md +36 -0
- data/Gemfile +1 -0
- data/ext/iodine/fio.c +9 -3
- data/ext/iodine/http.c +4 -0
- data/ext/iodine/http.h +4 -5
- data/ext/iodine/http_internal.h +3 -8
- data/ext/iodine/iodine_http.c +62 -81
- data/lib/iodine/version.rb +1 -1
- metadata +2 -2
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 15eb3d0d5e2834bc02a3e116dd07cd2fa36a73ad56ff7812579f1406a6d2874a
|
4
|
+
data.tar.gz: 3d9c061fe439e73a262a3c6ce0cd07f56d3ba2226b309caec539e202701e2249
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
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
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
|
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
|
-
|
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
|
};
|
data/ext/iodine/http_internal.h
CHANGED
@@ -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
|
-
|
128
|
+
h->fiber = NULL;
|
129
|
+
h->subscription = NULL;
|
135
130
|
|
136
131
|
*h = (http_s){
|
137
132
|
.private_data.vtbl = h->private_data.vtbl,
|
data/ext/iodine/iodine_http.c
CHANGED
@@ -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
|
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
|
-
|
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
|
-
|
683
|
-
|
684
|
-
|
685
|
-
|
686
|
-
|
687
|
-
|
688
|
-
|
689
|
-
|
690
|
-
|
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
|
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
|
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
|
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
|
773
|
+
return (void *)rbresponse;
|
773
774
|
|
774
775
|
defer:
|
775
776
|
IodineStore.remove(env);
|
776
|
-
|
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 *))
|
862
|
-
|
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
|
-
|
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
|
-
|
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
|
-
|
893
|
-
|
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
|
-
|
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");
|
data/lib/iodine/version.rb
CHANGED
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:
|
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
|
+
date: 2023-12-09 00:00:00.000000000 Z
|
12
12
|
dependencies:
|
13
13
|
- !ruby/object:Gem::Dependency
|
14
14
|
name: rake
|