rage-iodine 5.4.0 → 5.5.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 +10 -0
- data/ext/iodine/http.c +23 -3
- data/ext/iodine/http_internal.c +7 -5
- data/ext/iodine/iodine_caller.c +14 -0
- data/ext/iodine/iodine_caller.h +2 -0
- data/ext/iodine/iodine_defer.c +7 -1
- data/ext/iodine/iodine_http.c +29 -4
- data/lib/iodine/version.rb +1 -1
- metadata +1 -1
checksums.yaml
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
---
|
|
2
2
|
SHA256:
|
|
3
|
-
metadata.gz:
|
|
4
|
-
data.tar.gz:
|
|
3
|
+
metadata.gz: ce6be54873c53f93352aa7c739c74e73f5510eaeaab015bd2afc5e0fe261ff38
|
|
4
|
+
data.tar.gz: 45bba38d8c14f66e780981c13a05a7ca1d036a6809f71f33833e8743e285c32b
|
|
5
5
|
SHA512:
|
|
6
|
-
metadata.gz:
|
|
7
|
-
data.tar.gz:
|
|
6
|
+
metadata.gz: be4a8fd787b970fb21e30d6ec21e590ca27da1122d62f7efeac532f5b2fffd920c7016d489bd5c1e748e9d559058c23c3f567edc0e99e1da34a4a81b2bdde70c
|
|
7
|
+
data.tar.gz: 25e51f20a4a572caab13708ca5496e73cbcbb90810c13bf1000d8cbb0a1ec75bf797ade820d756a2c30237ac37c254e89303d0adeb7b6f060133a6cc1edc708e
|
data/CHANGELOG.md
CHANGED
|
@@ -6,6 +6,16 @@ Please notice that this change log contains changes for upcoming releases as wel
|
|
|
6
6
|
|
|
7
7
|
## Changes:
|
|
8
8
|
|
|
9
|
+
#### Change log v.5.5.0 (2026-07-06)
|
|
10
|
+
|
|
11
|
+
**Update**: Update `pre_start` callbacks to fail the server launch in case of errors
|
|
12
|
+
|
|
13
|
+
**Update**: Update media types in the MIME table
|
|
14
|
+
|
|
15
|
+
**Update**: Ensure users can override headers when serving files
|
|
16
|
+
|
|
17
|
+
**Update**: Support `x-sendfile-root` headers
|
|
18
|
+
|
|
9
19
|
#### Change log v.5.4.0 (2026-06-08)
|
|
10
20
|
|
|
11
21
|
**Update**: Optimize `fio_run_every` timers
|
data/ext/iodine/http.c
CHANGED
|
@@ -188,6 +188,26 @@ int http_set_header2(http_s *r, fio_str_info_s n, fio_str_info_s v) {
|
|
|
188
188
|
return ret;
|
|
189
189
|
}
|
|
190
190
|
|
|
191
|
+
/**
|
|
192
|
+
* Sets a response header if it hasn't already been set.
|
|
193
|
+
*
|
|
194
|
+
* Returns -1 on error and 0 on success.
|
|
195
|
+
*/
|
|
196
|
+
static inline int http_set_header_if_none(http_s *r, FIOBJ name, FIOBJ value) {
|
|
197
|
+
if (HTTP_INVALID_HANDLE(r) || !name) {
|
|
198
|
+
fiobj_free(value);
|
|
199
|
+
return -1;
|
|
200
|
+
}
|
|
201
|
+
|
|
202
|
+
if (fiobj_hash_get2(r->private_data.out_headers, fiobj_obj2hash(name))) {
|
|
203
|
+
fiobj_free(value);
|
|
204
|
+
return 0;
|
|
205
|
+
}
|
|
206
|
+
|
|
207
|
+
fiobj_hash_set(r->private_data.out_headers, name, value);
|
|
208
|
+
return 0;
|
|
209
|
+
}
|
|
210
|
+
|
|
191
211
|
/**
|
|
192
212
|
* Sets a response cookie, taking ownership of the value object, but NOT the
|
|
193
213
|
* name object (so name objects could be reused in future responses).
|
|
@@ -461,12 +481,12 @@ no_gzip_support:
|
|
|
461
481
|
return -1;
|
|
462
482
|
found_file:
|
|
463
483
|
/* set cache-control */
|
|
464
|
-
|
|
484
|
+
http_set_header_if_none(h, HTTP_HEADER_CACHE_CONTROL, fiobj_dup(HTTP_HVALUE_MAX_AGE));
|
|
465
485
|
/* set last-modified */
|
|
466
486
|
FIOBJ last_modified_str = fiobj_str_buf(32);
|
|
467
487
|
fiobj_str_resize(
|
|
468
488
|
last_modified_str, http_time2str(fiobj_obj2cstr(last_modified_str).data, file_data.st_mtime));
|
|
469
|
-
|
|
489
|
+
http_set_header_if_none(h, HTTP_HEADER_LAST_MODIFIED, last_modified_str);
|
|
470
490
|
/* set & test etag */
|
|
471
491
|
FIOBJ etag_str = fiobj_str_buf(1);
|
|
472
492
|
fiobj_str_printf(etag_str, "%lx-%llx", file_data.st_mtime, file_data.st_size);
|
|
@@ -605,7 +625,7 @@ open_file:
|
|
|
605
625
|
tmp = http_mimetype_find(s.data + pos, s.len - pos);
|
|
606
626
|
}
|
|
607
627
|
if (tmp)
|
|
608
|
-
|
|
628
|
+
http_set_header_if_none(h, HTTP_HEADER_CONTENT_TYPE, tmp);
|
|
609
629
|
}
|
|
610
630
|
http_sendfile(h, file, length, offset);
|
|
611
631
|
return 0;
|
data/ext/iodine/http_internal.c
CHANGED
|
@@ -610,7 +610,7 @@ static void http_lib_init(void *ignr_) {
|
|
|
610
610
|
REGISTER_MIME("jpgm", "video/jpm");
|
|
611
611
|
REGISTER_MIME("jpgv", "video/jpeg");
|
|
612
612
|
REGISTER_MIME("jpm", "video/jpm");
|
|
613
|
-
REGISTER_MIME("js", "
|
|
613
|
+
REGISTER_MIME("js", "text/javascript");
|
|
614
614
|
REGISTER_MIME("json", "application/json");
|
|
615
615
|
REGISTER_MIME("jsonml", "application/jsonml+json");
|
|
616
616
|
REGISTER_MIME("kar", "audio/midi");
|
|
@@ -694,6 +694,7 @@ static void http_lib_init(void *ignr_) {
|
|
|
694
694
|
REGISTER_MIME("mie", "application/x-mie");
|
|
695
695
|
REGISTER_MIME("mif", "application/vnd.mif");
|
|
696
696
|
REGISTER_MIME("mime", "message/rfc822");
|
|
697
|
+
REGISTER_MIME("mjs", "text/javascript");
|
|
697
698
|
REGISTER_MIME("mj2", "video/mj2");
|
|
698
699
|
REGISTER_MIME("mjp2", "video/mj2");
|
|
699
700
|
REGISTER_MIME("mk3d", "video/x-matroska");
|
|
@@ -803,7 +804,7 @@ static void http_lib_init(void *ignr_) {
|
|
|
803
804
|
REGISTER_MIME("osf", "application/vnd.yamaha.openscoreformat");
|
|
804
805
|
REGISTER_MIME("osfpvg", "application/vnd.yamaha.openscoreformat.osfpvg+xml");
|
|
805
806
|
REGISTER_MIME("otc", "application/vnd.oasis.opendocument.chart-template");
|
|
806
|
-
REGISTER_MIME("otf", "
|
|
807
|
+
REGISTER_MIME("otf", "font/otf");
|
|
807
808
|
REGISTER_MIME("otg", "application/vnd.oasis.opendocument.graphics-template");
|
|
808
809
|
REGISTER_MIME("oth", "application/vnd.oasis.opendocument.text-web");
|
|
809
810
|
REGISTER_MIME("oti", "application/vnd.oasis.opendocument.image-template");
|
|
@@ -1066,8 +1067,8 @@ static void http_lib_init(void *ignr_) {
|
|
|
1066
1067
|
REGISTER_MIME("trm", "application/x-msterminal");
|
|
1067
1068
|
REGISTER_MIME("tsd", "application/timestamped-data");
|
|
1068
1069
|
REGISTER_MIME("tsv", "text/tab-separated-values");
|
|
1069
|
-
REGISTER_MIME("ttc", "
|
|
1070
|
-
REGISTER_MIME("ttf", "
|
|
1070
|
+
REGISTER_MIME("ttc", "font/collection");
|
|
1071
|
+
REGISTER_MIME("ttf", "font/ttf");
|
|
1071
1072
|
REGISTER_MIME("ttl", "text/turtle");
|
|
1072
1073
|
REGISTER_MIME("twd", "application/vnd.simtech-mindmapper");
|
|
1073
1074
|
REGISTER_MIME("twds", "application/vnd.simtech-mindmapper");
|
|
@@ -1163,7 +1164,8 @@ static void http_lib_init(void *ignr_) {
|
|
|
1163
1164
|
REGISTER_MIME("wmx", "video/x-ms-wmx");
|
|
1164
1165
|
REGISTER_MIME("wmz", "application/x-ms-wmz");
|
|
1165
1166
|
// REGISTER_MIME("wmz", "application/x-msmetafile");
|
|
1166
|
-
REGISTER_MIME("woff", "
|
|
1167
|
+
REGISTER_MIME("woff", "font/woff");
|
|
1168
|
+
REGISTER_MIME("woff2", "font/woff2");
|
|
1167
1169
|
REGISTER_MIME("wpd", "application/vnd.wordperfect");
|
|
1168
1170
|
REGISTER_MIME("wpl", "application/vnd.ms-wpl");
|
|
1169
1171
|
REGISTER_MIME("wps", "application/vnd.ms-works");
|
data/ext/iodine/iodine_caller.c
CHANGED
|
@@ -152,6 +152,18 @@ static VALUE iodine_call(VALUE obj, ID method) {
|
|
|
152
152
|
return (VALUE)rv;
|
|
153
153
|
}
|
|
154
154
|
|
|
155
|
+
/** Calls a Ruby method on a given object, without protecting against exceptions. */
|
|
156
|
+
static VALUE iodine_call_unprotected(VALUE obj, ID method) {
|
|
157
|
+
iodine_rb_task_s task = {
|
|
158
|
+
.obj = obj,
|
|
159
|
+
.argc = 0,
|
|
160
|
+
.argv = NULL,
|
|
161
|
+
.method = method,
|
|
162
|
+
};
|
|
163
|
+
void *rv = iodine_enterGVL((void *(*)(void *))iodine_ruby_caller_perform, &task);
|
|
164
|
+
return (VALUE)rv;
|
|
165
|
+
}
|
|
166
|
+
|
|
155
167
|
/** Calls a Ruby method on a given object, protecting against exceptions. */
|
|
156
168
|
static VALUE iodine_call2(VALUE obj, ID method, int argc, VALUE *argv) {
|
|
157
169
|
iodine_rb_task_s task = {
|
|
@@ -223,6 +235,8 @@ struct IodineCaller_s IodineCaller = {
|
|
|
223
235
|
.call = iodine_call,
|
|
224
236
|
/** Calls a Ruby method on a given object, protecting against exceptions. */
|
|
225
237
|
.call2 = iodine_call2,
|
|
238
|
+
/** Calls a Ruby method on a given object, without protecting against exceptions. */
|
|
239
|
+
.call_unprotected = iodine_call_unprotected,
|
|
226
240
|
/** Returns the GVL state flag. */
|
|
227
241
|
.in_GVL = iodine_in_GVL,
|
|
228
242
|
/** Forces the GVL state flag. */
|
data/ext/iodine/iodine_caller.h
CHANGED
|
@@ -14,6 +14,8 @@ extern struct IodineCaller_s {
|
|
|
14
14
|
VALUE (*call)(VALUE obj, ID method);
|
|
15
15
|
/** Calls a Ruby method on a given object, protecting against exceptions. */
|
|
16
16
|
VALUE (*call2)(VALUE obj, ID method, int argc, VALUE *argv);
|
|
17
|
+
/** Calls a Ruby method on a given object, without protecting against exceptions. */
|
|
18
|
+
VALUE (*call_unprotected)(VALUE obj, ID method);
|
|
17
19
|
/** Calls a Ruby method on a given object, protecting against exceptions. */
|
|
18
20
|
VALUE(*call_with_block)
|
|
19
21
|
(VALUE obj, ID method, int argc, VALUE *argv, VALUE udata,
|
data/ext/iodine/iodine_defer.c
CHANGED
|
@@ -323,6 +323,12 @@ static void iodine_perform_state_callback_persist(void *blk_) {
|
|
|
323
323
|
IodineCaller.call(blk, call_id);
|
|
324
324
|
}
|
|
325
325
|
|
|
326
|
+
/* performs a Ruby state callback without protecting against exceptions */
|
|
327
|
+
static void iodine_perform_unprotected_state_callback_persist(void *blk_) {
|
|
328
|
+
VALUE blk = (VALUE)blk_;
|
|
329
|
+
IodineCaller.call_unprotected(blk, call_id);
|
|
330
|
+
}
|
|
331
|
+
|
|
326
332
|
// clang-format off
|
|
327
333
|
/**
|
|
328
334
|
Sets a block of code to run when Iodine's core state is updated.
|
|
@@ -357,7 +363,7 @@ static VALUE iodine_on_state(VALUE self, VALUE event) {
|
|
|
357
363
|
|
|
358
364
|
if (state == STATE_PRE_START) {
|
|
359
365
|
fio_state_callback_add(FIO_CALL_PRE_START,
|
|
360
|
-
|
|
366
|
+
iodine_perform_unprotected_state_callback_persist,
|
|
361
367
|
(void *)block);
|
|
362
368
|
} else if (state == STATE_BEFORE_FORK) {
|
|
363
369
|
fio_state_callback_add(FIO_CALL_BEFORE_FORK,
|
data/ext/iodine/iodine_http.c
CHANGED
|
@@ -102,6 +102,7 @@ rack_declare(CONTENT_TYPE);
|
|
|
102
102
|
rack_declare(R_URL_SCHEME); // rack.url_scheme
|
|
103
103
|
rack_declare(R_INPUT); // rack.input
|
|
104
104
|
rack_declare(XSENDFILE); // for X-Sendfile support
|
|
105
|
+
rack_declare(XSENDFILE_ROOT); // for X-Sendfile support
|
|
105
106
|
rack_declare(XSENDFILE_TYPE); // for X-Sendfile support
|
|
106
107
|
rack_declare(XSENDFILE_TYPE_HEADER); // for X-Sendfile support
|
|
107
108
|
rack_declare(CONTENT_LENGTH_HEADER); // for X-Sendfile support
|
|
@@ -112,6 +113,7 @@ rack_declare(IODINE_HAS_BODY);
|
|
|
112
113
|
typedef struct {
|
|
113
114
|
http_s *h;
|
|
114
115
|
FIOBJ body;
|
|
116
|
+
FIOBJ root;
|
|
115
117
|
enum iodine_http_response_type_enum {
|
|
116
118
|
IODINE_HTTP_NONE,
|
|
117
119
|
IODINE_HTTP_SENDBODY,
|
|
@@ -716,6 +718,14 @@ static inline void *iodine_handle_request_in_GVL(void *handle_) {
|
|
|
716
718
|
handle->body = fiobj_str_new(RSTRING_PTR(xfiles), RSTRING_LEN(xfiles));
|
|
717
719
|
handle->type = IODINE_HTTP_XSENDFILE;
|
|
718
720
|
rb_hash_delete(response_headers, XSENDFILE);
|
|
721
|
+
// extract optional root directory for path validation
|
|
722
|
+
VALUE xroot = rb_hash_aref(response_headers, XSENDFILE_ROOT);
|
|
723
|
+
if (xroot != Qnil && TYPE(xroot) == T_STRING) {
|
|
724
|
+
handle->root = fiobj_str_new(RSTRING_PTR(xroot), RSTRING_LEN(xroot));
|
|
725
|
+
rb_hash_delete(response_headers, XSENDFILE_ROOT);
|
|
726
|
+
} else {
|
|
727
|
+
handle->root = FIOBJ_INVALID;
|
|
728
|
+
}
|
|
719
729
|
// remove content length headers, as this will be controled by iodine
|
|
720
730
|
rb_hash_delete(response_headers, CONTENT_LENGTH_HEADER);
|
|
721
731
|
// review each header and write it to the response.
|
|
@@ -782,8 +792,22 @@ iodine_perform_handle_action(iodine_http_request_handle_s handle) {
|
|
|
782
792
|
.len == 7)
|
|
783
793
|
fiobj_hash_delete2(handle.h->private_data.out_headers,
|
|
784
794
|
fiobj_obj2hash(HTTP_HEADER_CONTENT_ENCODING));
|
|
785
|
-
fio_str_info_s
|
|
786
|
-
|
|
795
|
+
fio_str_info_s file_data = fiobj_obj2cstr(handle.body);
|
|
796
|
+
int err;
|
|
797
|
+
if (handle.root != FIOBJ_INVALID) {
|
|
798
|
+
/* root dir specified - treat x-sendfile as relative/encoded path */
|
|
799
|
+
fio_str_info_s root_data = fiobj_obj2cstr(handle.root);
|
|
800
|
+
err = http_sendfile2(handle.h, root_data.data, root_data.len,
|
|
801
|
+
file_data.data, file_data.len);
|
|
802
|
+
fiobj_free(handle.root);
|
|
803
|
+
} else {
|
|
804
|
+
/* no root dir - original behavior (absolute path as prefix) */
|
|
805
|
+
err = http_sendfile2(handle.h, file_data.data, file_data.len, NULL, 0);
|
|
806
|
+
}
|
|
807
|
+
if (err) {
|
|
808
|
+
fiobj_hash_clear(handle.h->private_data.out_headers);
|
|
809
|
+
http_set_header(handle.h, HTTP_HEADER_CACHE_CONTROL,
|
|
810
|
+
fiobj_str_new("no-cache, max-age=0", 19));
|
|
787
811
|
http_send_error(handle.h, 404);
|
|
788
812
|
}
|
|
789
813
|
fiobj_free(handle.body);
|
|
@@ -1181,10 +1205,11 @@ void iodine_init_http(void) {
|
|
|
1181
1205
|
rack_set(QUERY_ESTRING, "");
|
|
1182
1206
|
rack_set(R_URL_SCHEME, "rack.url_scheme");
|
|
1183
1207
|
rack_set(R_INPUT, "rack.input");
|
|
1184
|
-
rack_set(XSENDFILE, "
|
|
1208
|
+
rack_set(XSENDFILE, "x-sendfile");
|
|
1209
|
+
rack_set(XSENDFILE_ROOT, "x-sendfile-root");
|
|
1185
1210
|
rack_set(XSENDFILE_TYPE, "sendfile.type");
|
|
1186
1211
|
rack_set(XSENDFILE_TYPE_HEADER, "HTTP_X_SENDFILE_TYPE");
|
|
1187
|
-
rack_set(CONTENT_LENGTH_HEADER, "
|
|
1212
|
+
rack_set(CONTENT_LENGTH_HEADER, "content-length");
|
|
1188
1213
|
|
|
1189
1214
|
rack_set(IODINE_R_INPUT, "rack.input");
|
|
1190
1215
|
rack_set(IODINE_R_HIJACK_IO, "rack.hijack_io");
|
data/lib/iodine/version.rb
CHANGED