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 +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
|