rage-iodine 5.4.0 → 5.6.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: 9ef6e88ecbc5910815537ef78329c6bc965261fb4514c479be7594fb6de61dca
4
- data.tar.gz: '09632895e2ed3a00f6300a418c46b7f10332403f83aceeaffe77af57258f9baf'
3
+ metadata.gz: 7ffa46cab7e0fb7fc7550dd142f42b0850287fee60cc0d72b7f52757f52720f7
4
+ data.tar.gz: c51b8ad9e316c21ed06e6803dcfb5af7bf70fc3eee84a0fd179b2730d2572473
5
5
  SHA512:
6
- metadata.gz: 6ce3edb67d9029b9a9bf73a941dcbcd7378214246cbbb5a09084f085dda5c2b41003dc75338a595f054e28db146cc713e887a3d43fffebefc80567d87348a94a
7
- data.tar.gz: 3b2f347ecc8732f23ecac55bbc5c8c3d9d51b6e814bbb9b87f23d538f211ab6a605972b879f4449c3ce10fe95bc1920930304e604f53f5f593d5d889f4095c25
6
+ metadata.gz: fa99ee4e7abb10b55b6f14aeeaef6bf03e85cf8aff9b33a66f7b4f64097c5ef824e0c3de6663caf00f5404b2d08b663debb60c796a0a44490138a96ac92a832c
7
+ data.tar.gz: ca2a4ac6f633955b202dccf09661cb7799ae92e16078ef3776c1648e18e2b1ab620f470f4b99b3c75b7d0e8f9b064fad180fd5c9b32ab6ae0ddd60c9d601b58d
data/CHANGELOG.md CHANGED
@@ -6,6 +6,20 @@ 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.6.0 (2026-09-06)
10
+
11
+ **Update**: TCP Socket Listen: Accept Queue
12
+
13
+ #### Change log v.5.5.0 (2026-07-06)
14
+
15
+ **Update**: Update `pre_start` callbacks to fail the server launch in case of errors
16
+
17
+ **Update**: Update media types in the MIME table
18
+
19
+ **Update**: Ensure users can override headers when serving files
20
+
21
+ **Update**: Support `x-sendfile-root` headers
22
+
9
23
  #### Change log v.5.4.0 (2026-06-08)
10
24
 
11
25
  **Update**: Optimize `fio_run_every` timers
data/Gemfile CHANGED
@@ -4,7 +4,8 @@ group :test do
4
4
  gem 'pry'
5
5
  gem 'rspec'
6
6
  gem 'rack'
7
- gem 'http'
7
+ gem 'http', '~> 5.0'
8
+ gem 'ostruct', '~> 0.6.3'
8
9
  gem 'base64'
9
10
  gem 'benchmark'
10
11
  end
data/ext/iodine/fio.c CHANGED
@@ -4532,10 +4532,22 @@ Initialize the library
4532
4532
 
4533
4533
  static void fio_pubsub_on_fork(void);
4534
4534
 
4535
+ #if defined(__linux__)
4536
+ /* accept-queue tracking lock for the listener registry (defined alongside
4537
+ * fio_accept_queue_listeners in the listener section below); declared here
4538
+ * so fio_on_fork can reset it across children. */
4539
+ static fio_lock_i fio_accept_queue_lock;
4540
+ #endif
4541
+
4535
4542
  /* Called within a child process after it starts. */
4536
4543
  static void fio_on_fork(void) {
4537
4544
  fio_timer_lock = FIO_LOCK_INIT;
4538
4545
  fio_data->lock = FIO_LOCK_INIT;
4546
+ #if defined(__linux__)
4547
+ /* a forked child may inherit the accept-queue lock while a parent thread
4548
+ * held it mid-walk; that thread won't exist in the child, so reset it. */
4549
+ fio_accept_queue_lock = FIO_LOCK_INIT;
4550
+ #endif
4539
4551
  fio_defer_on_fork();
4540
4552
  fio_malloc_after_fork();
4541
4553
  fio_poll_init();
@@ -5571,6 +5583,14 @@ The listening protocol (use the facil.io API to make a socket and attach it)
5571
5583
  typedef struct {
5572
5584
  fio_protocol_s pr;
5573
5585
  intptr_t uuid;
5586
+ #if defined(__linux__)
5587
+ /**
5588
+ * Listener node adding this listener into to fio_accept_queue_listeners,
5589
+ * so Iodine::Perf.queued_connections can find its socket's fd and read its
5590
+ * kernel length accept-queue(AcceptQ):backlog where (backlog <= somaxconn) (TCP_INFO).
5591
+ */
5592
+ fio_ls_embd_s listener_node;
5593
+ #endif
5574
5594
  void *udata;
5575
5595
  void (*on_open)(intptr_t uuid, void *udata);
5576
5596
  void (*on_start)(intptr_t uuid, void *udata);
@@ -5582,8 +5602,20 @@ typedef struct {
5582
5602
  void *tls;
5583
5603
  } fio_listen_protocol_s;
5584
5604
 
5605
+ #if defined(__linux__)
5606
+ /* tracks iodine's listening sockets for the accept-queue backlog query.
5607
+ * guarded against concurrent teardown (reactor thread, no GVL) */
5608
+ static fio_ls_embd_s fio_accept_queue_listeners = FIO_LS_INIT(fio_accept_queue_listeners);
5609
+ static fio_lock_i fio_accept_queue_lock = FIO_LOCK_INIT;
5610
+ #endif
5611
+
5585
5612
  static void fio_listen_cleanup_task(void *pr_) {
5586
5613
  fio_listen_protocol_s *pr = pr_;
5614
+ #if defined(__linux__)
5615
+ fio_lock(&fio_accept_queue_lock);
5616
+ fio_ls_embd_remove(&pr->listener_node);
5617
+ fio_unlock(&fio_accept_queue_lock);
5618
+ #endif
5587
5619
  #ifndef __MINGW32__
5588
5620
  if (pr->tls)
5589
5621
  fio_tls_destroy(pr->tls);
@@ -5733,6 +5765,12 @@ intptr_t fio_listen FIO_IGNORE_MACRO(struct fio_listen_args args) {
5733
5765
  if (port_len)
5734
5766
  memcpy(pr->port, args.port, port_len + 1);
5735
5767
 
5768
+ #if defined(__linux__)
5769
+ fio_lock(&fio_accept_queue_lock);
5770
+ fio_ls_embd_push(&fio_accept_queue_listeners, &pr->listener_node);
5771
+ fio_unlock(&fio_accept_queue_lock);
5772
+ #endif
5773
+
5736
5774
  if (fio_is_running()) {
5737
5775
  fio_attach(pr->uuid, &pr->pr);
5738
5776
  } else {
@@ -5753,6 +5791,65 @@ error:
5753
5791
  return -1;
5754
5792
  }
5755
5793
 
5794
+ /* -------------------------------------------------------------------------
5795
+ * Kernel accept-queue(AcceptQ) which is Queue length: backlog, where backlog <= somaxconn
5796
+ * "backlog are established connections waiting for accept()" = sk_ack_backlog.
5797
+ * Returns the sum of the accept-queues of all iodine listening sockets, or 0.
5798
+ * ---------------------------------------------------------------------- */
5799
+ #if defined(__linux__)
5800
+ intptr_t fio_queued_connections(void) {
5801
+ intptr_t backlog = 0;
5802
+
5803
+ fio_lock(&fio_accept_queue_lock);
5804
+ /**
5805
+ * Walk every registered listener and sum its kernel accept-queue depth/length.
5806
+ * The list always begins with a permanent head node (fio_accept_queue_listeners)
5807
+ * that works like a fixed bookmark: it holds no listener of its own, real nodes
5808
+ * are chained after it, and the last node links back to it, forming a circle
5809
+ * i.e a circular linked list also it's a doubly linked list.
5810
+ *
5811
+ * So the loop:
5812
+ * - starts at `.next`, i.e. the FIRST REAL node starting at the head
5813
+ * itself would be wrong, since it owns no listener and converting it
5814
+ * with FIO_LS_EMBD_OBJ would produce a bogus pointer;
5815
+ * - stops when it arrives back at the head, meaning every real node
5816
+ * has been visited;
5817
+ * - runs zero times on an empty list, because a lone head simply
5818
+ * points at itself.
5819
+ */
5820
+ for (fio_ls_embd_s *pos = fio_accept_queue_listeners.next; pos != &fio_accept_queue_listeners; pos = pos->next) {
5821
+ fio_listen_protocol_s *pr = FIO_LS_EMBD_OBJ(fio_listen_protocol_s, listener_node, pos);
5822
+ struct tcp_info info;
5823
+ socklen_t tlen = sizeof(info);
5824
+ int fd = fio_uuid2fd(pr->uuid);
5825
+
5826
+ fio_lock(&fd_data(fd).protocol_lock);
5827
+
5828
+ /* Skip sockets that were closed or reused before we could query them.
5829
+ * Callers must run this after listeners are attached (e.g. from within
5830
+ * Iodine.run / a background thread), so the listener's protocol slot is
5831
+ * populated and the checks below are meaningful. */
5832
+ if (!uuid_is_valid(pr->uuid) || fd_data(fd).protocol != &pr->pr) {
5833
+ fio_unlock(&fd_data(fd).protocol_lock);
5834
+ continue;
5835
+ }
5836
+
5837
+ int ok = !getsockopt(fd, IPPROTO_TCP, TCP_INFO, &info, &tlen);
5838
+
5839
+ fio_unlock(&fd_data(fd).protocol_lock);
5840
+
5841
+ if (!ok) {
5842
+ continue;
5843
+ }
5844
+
5845
+ backlog += (intptr_t)info.tcpi_unacked;
5846
+ }
5847
+ fio_unlock(&fio_accept_queue_lock);
5848
+
5849
+ return backlog;
5850
+ }
5851
+ #endif
5852
+
5756
5853
  /* *****************************************************************************
5757
5854
  Section Start Marker
5758
5855
 
data/ext/iodine/fio.h CHANGED
@@ -1111,6 +1111,19 @@ intptr_t fio_socket(const char *address, const char *port, uint8_t is_server);
1111
1111
  */
1112
1112
  intptr_t fio_accept(intptr_t srv_uuid);
1113
1113
 
1114
+ /**
1115
+ * Returns the number of established connections currently parked in the
1116
+ * kernel's accept queue(s) (sk_ack_backlog) of all iodine listening sockets.
1117
+ *
1118
+ * The query goes straight to the kernel via getsockopt(TCP_INFO).
1119
+ *
1120
+ * Returns the total accept queue length across all iodine listeners, or 0
1121
+ * when there are no listeners.
1122
+ */
1123
+ #if defined(__linux__)
1124
+ intptr_t fio_queued_connections(void);
1125
+ #endif /* linux only */
1126
+
1114
1127
  /**
1115
1128
  * Returns 1 if the uuid refers to a valid and open, socket.
1116
1129
  *
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
- http_set_header(h, HTTP_HEADER_CACHE_CONTROL, fiobj_dup(HTTP_HVALUE_MAX_AGE));
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
- http_set_header(h, HTTP_HEADER_LAST_MODIFIED, last_modified_str);
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
- http_set_header(h, HTTP_HEADER_CONTENT_TYPE, tmp);
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;
@@ -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", "application/javascript");
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", "application/x-font-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", "application/x-font-ttf");
1070
- REGISTER_MIME("ttf", "application/x-font-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", "application/font-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.c CHANGED
@@ -1401,6 +1401,9 @@ void Init_iodine_ext(void) {
1401
1401
  // initialize concurrency related methods
1402
1402
  iodine_defer_initialize();
1403
1403
 
1404
+ // initialize performance metrics
1405
+ iodine_perf_initialize();
1406
+
1404
1407
  // initialize the connection class
1405
1408
  iodine_connection_init();
1406
1409
 
data/ext/iodine/iodine.h CHANGED
@@ -42,6 +42,7 @@ typedef struct {
42
42
  #include "iodine_http.h"
43
43
  #include "iodine_json.h"
44
44
  #include "iodine_mustache.h"
45
+ #include "iodine_perf.h"
45
46
  #include "iodine_pubsub.h"
46
47
  #include "iodine_rack_io.h"
47
48
  #include "iodine_store.h"
@@ -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. */
@@ -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,
@@ -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
- iodine_perform_state_callback_persist,
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,
@@ -409,6 +415,7 @@ static void iodine_defer_on_finish(void *ignr) {
409
415
  iodine_join_io_thread();
410
416
  }
411
417
 
418
+
412
419
  /* *****************************************************************************
413
420
  Add defer API to Iodine
414
421
  ***************************************************************************** */
@@ -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 data = fiobj_obj2cstr(handle.body);
786
- if (http_sendfile2(handle.h, data.data, data.len, NULL, 0)) {
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, "X-Sendfile");
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, "Content-Length");
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");
@@ -0,0 +1,28 @@
1
+ #include "iodine.h"
2
+
3
+ /* *****************************************************************************
4
+ Performance metrics
5
+ ***************************************************************************** */
6
+
7
+ /**
8
+ * Iodine::Perf.queued_connections
9
+ *
10
+ * Returns the total number of established connections currently parked in
11
+ * the kernel's accept queue(s) (sk_ack_backlog) for all iodine listening
12
+ * sockets. Returns 0 when there are no listeners and nil on non-Linux systems.
13
+ */
14
+ static VALUE iodine_perf_queued_connections(VALUE self) {
15
+ (void)self;
16
+ #if defined(__linux__)
17
+ intptr_t backlog = fio_queued_connections();
18
+ return INT2NUM((long)backlog);
19
+ #else
20
+ return Qnil;
21
+ #endif
22
+ }
23
+
24
+ void iodine_perf_initialize(void) {
25
+ VALUE IodinePerfModule = rb_define_module_under(IodineModule, "Perf");
26
+ rb_define_module_function(IodinePerfModule, "queued_connections",
27
+ iodine_perf_queued_connections, 0);
28
+ }
@@ -0,0 +1,6 @@
1
+ #ifndef H_IODINE_PERF_H
2
+ #define H_IODINE_PERF_H
3
+
4
+ void iodine_perf_initialize(void);
5
+
6
+ #endif
@@ -1,3 +1,3 @@
1
1
  module Iodine
2
- VERSION = '5.4.0'.freeze
2
+ VERSION = '5.6.0'.freeze
3
3
  end
metadata CHANGED
@@ -1,7 +1,7 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: rage-iodine
3
3
  version: !ruby/object:Gem::Version
4
- version: 5.4.0
4
+ version: 5.6.0
5
5
  platform: ruby
6
6
  authors:
7
7
  - Boaz Segev
@@ -217,6 +217,8 @@ files:
217
217
  - ext/iodine/iodine_json.h
218
218
  - ext/iodine/iodine_mustache.c
219
219
  - ext/iodine/iodine_mustache.h
220
+ - ext/iodine/iodine_perf.c
221
+ - ext/iodine/iodine_perf.h
220
222
  - ext/iodine/iodine_pubsub.c
221
223
  - ext/iodine/iodine_pubsub.h
222
224
  - ext/iodine/iodine_rack_io.c