Module: Iodine::Rack::Utils
- Defined in:
- lib/iodine/rack_utils.rb,
ext/iodine/iodine_helpers.c
Overview
Choosing to monkey patch Rack::Utils could offer significant performance gains for some applications. i.e. (on my machine):
require 'iodine'
require 'rack'
# a String in need of decoding
s = '%E3%83%AB%E3%83%93%E3%82%A4%E3%82%B9%E3%81%A8'
Benchmark.bm do |bm|
# Pre-Patch
bm.report(" Rack.unescape") {1_000_000.times { Rack::Utils.unescape s } }
bm.report(" Rack.rfc2822") {1_000_000.times { Rack::Utils.rfc2822(Time.now) } }
bm.report(" Rack.rfc2109") {1_000_000.times { Rack::Utils.rfc2109(Time.now) } }
# Perform Patch
Iodine.patch_rack
puts " --- Monkey Patching Rack ---"
# Post Patch
bm.report("Patched.unescape") {1_000_000.times { Rack::Utils.unescape s } }
bm.report(" Patched.rfc2822") {1_000_000.times { Rack::Utils.rfc2822(Time.now) } }
bm.report(" Patched.rfc2109") {1_000_000.times { Rack::Utils.rfc2109(Time.now) } }
end && nil
Results:
user system total real
Rack.unescape 8.706881 0.019995 8.726876 ( 8.740530)
Rack.rfc2822 3.270305 0.007519 3.277824 ( 3.279416)
Rack.rfc2109 3.152188 0.003852 3.156040 ( 3.157975)
--- Monkey Patching Rack ---
Patched.unescape 0.327231 0.003125 0.330356 ( 0.337090)
Patched.rfc2822 0.691304 0.003330 0.694634 ( 0.701172)
Patched.rfc2109 0.685029 0.001956 0.686985 ( 0.687607)
Iodine uses the same code internally for HTTP timestamping (adding missing Date
headers) and logging.
Class Method Summary collapse
-
.decode_path(str) ⇒ Object
Decodes a percent encoded String (normally the "path" of a request), returning a new String with the decoded data.
-
.decode_path!(str) ⇒ Object
Decodes a percent encoded String (normally the "path" of a request), editing the String in place.
-
.decode_url(str) ⇒ Object
Decodes a URL encoded String, returning a new String with the decoded data.
-
.decode_url!(str) ⇒ Object
Decodes a URL encoded String in place.
-
.gen_request_tag ⇒ Object
Generate a request tag to use in logs.
-
.gen_timestamp ⇒ Object
Generate a timestamp to use in logs.
-
.parse_multipart(rack_io, content_type) ⇒ Object
Convert multipart/form-data into a Ruby object.
-
.parse_nested_query(r_str) ⇒ Object
Convert query string into a Ruby object.
-
.parse_urlencoded_nested_query(r_str) ⇒ Object
Convert urlencoded body into a Ruby object.
-
.rfc2109(rtm) ⇒ Object
Takes
time
and returns a faster (though less localized) HTTP Date formatted String. -
.rfc2822(rtm) ⇒ Object
Takes
time
and returns a faster (though less localized) HTTP Date formatted String. -
.time2str(*args) ⇒ Object
Takes an optional Integer for Unix Time and returns a faster (though less localized) HTTP Date formatted String.
Class Method Details
.decode_path(str) ⇒ Object
Decodes a percent encoded String (normally the "path" of a request), returning a new String with the decoded data.
79 80 81 82 83 84 85 86 87 88 89 |
# File 'ext/iodine/iodine_helpers.c', line 79
static VALUE path_decode(VALUE self, VALUE str) {
Check_Type(str, T_STRING);
VALUE str2 = rb_str_buf_new(RSTRING_LEN(str));
ssize_t len =
http_decode_path(RSTRING_PTR(str2), RSTRING_PTR(str), RSTRING_LEN(str));
if (len < 0)
rb_raise(rb_eRuntimeError, "Malformed URL path string - couldn't decode.");
rb_str_set_len(str2, len);
return str2;
(void)self;
}
|
.decode_path!(str) ⇒ Object
Decodes a percent encoded String (normally the "path" of a request), editing the String in place.
Raises an exception on error... but this might result in a partially decoded String.
62 63 64 65 66 67 68 69 70 71 72 73 |
# File 'ext/iodine/iodine_helpers.c', line 62
static VALUE path_decode_inplace(VALUE self, VALUE str) {
Check_Type(str, T_STRING);
ssize_t len =
http_decode_path(RSTRING_PTR(str), RSTRING_PTR(str), RSTRING_LEN(str));
if (len < 0)
rb_raise(rb_eRuntimeError,
"Malformed URL path string - couldn't decode (String "
"might have been partially altered).");
rb_str_set_len(str, len);
return str;
(void)self;
}
|
.decode_url(str) ⇒ Object
Decodes a URL encoded String, returning a new String with the decoded data.
43 44 45 46 47 48 49 50 51 52 53 |
# File 'ext/iodine/iodine_helpers.c', line 43
static VALUE url_decode(VALUE self, VALUE str) {
Check_Type(str, T_STRING);
VALUE str2 = rb_str_buf_new(RSTRING_LEN(str));
ssize_t len =
http_decode_url(RSTRING_PTR(str2), RSTRING_PTR(str), RSTRING_LEN(str));
if (len < 0)
rb_raise(rb_eRuntimeError, "Malformed URL string - couldn't decode.");
rb_str_set_len(str2, len);
return str2;
(void)self;
}
|
.decode_url!(str) ⇒ Object
Decodes a URL encoded String in place.
Raises an exception on error... but this might result in a partially decoded String.
28 29 30 31 32 33 34 35 36 37 38 |
# File 'ext/iodine/iodine_helpers.c', line 28
static VALUE url_decode_inplace(VALUE self, VALUE str) {
Check_Type(str, T_STRING);
ssize_t len =
http_decode_url(RSTRING_PTR(str), RSTRING_PTR(str), RSTRING_LEN(str));
if (len < 0)
rb_raise(rb_eRuntimeError, "Malformed URL string - couldn't decode (String "
"might have been partially altered).");
rb_str_set_len(str, len);
return str;
(void)self;
}
|
.gen_request_tag ⇒ Object
Generate a request tag to use in logs.
479 480 481 482 483 484 485 486 487 488 489 |
# File 'ext/iodine/iodine_helpers.c', line 479
static VALUE gen_request_tag(VALUE self) {
char buffer[request_tag_len];
uint8_t i, random_index;
for (i = 0; i < request_tag_len; i++) {
random_index = rand() % 36;
buffer[i] = request_tag_seed[random_index];
}
return rb_str_new(buffer, request_tag_len);
}
|
.gen_timestamp ⇒ Object
Generate a timestamp to use in logs.
462 463 464 465 466 467 468 469 470 471 |
# File 'ext/iodine/iodine_helpers.c', line 462 static VALUE (VALUE self) { struct tm tm; time_t last_tick = fio_last_tick().tv_sec; http_gmtime(last_tick, &tm); char buffer[32]; size_t len = (buffer, &tm); return rb_str_new(buffer, len); } |
.parse_multipart(rack_io, content_type) ⇒ Object
Convert multipart/form-data into a Ruby object.
448 449 450 451 452 453 454 455 456 457 |
# File 'ext/iodine/iodine_helpers.c', line 448
static VALUE parse_multipart(VALUE self, VALUE rack_io, VALUE content_type) {
http_s *h = IodineRackIO.get_handle(rack_io);
if (content_type == Qnil) {
rb_raise(rb_eRuntimeError, "Incorrect content type for multipart request");
}
return http_parse_multipart(h, RSTRING_PTR(content_type), RSTRING_LEN(content_type));
(void)self;
}
|
.parse_nested_query(r_str) ⇒ Object
Convert query string into a Ruby object.
427 428 429 430 |
# File 'ext/iodine/iodine_helpers.c', line 427
static VALUE parse_nested_query(VALUE self, VALUE r_str) {
return parse_nested_query_internal(RSTRING_PTR(r_str), RSTRING_LEN(r_str));
(void)self;
}
|
.parse_urlencoded_nested_query(r_str) ⇒ Object
Convert urlencoded body into a Ruby object.
435 436 437 438 439 440 441 442 443 |
# File 'ext/iodine/iodine_helpers.c', line 435
static VALUE parse_urlencoded_nested_query(VALUE self, VALUE r_str) {
ssize_t len = http_decode_url(RSTRING_PTR(r_str), RSTRING_PTR(r_str), RSTRING_LEN(r_str));
if (len < 0) {
rb_raise(rb_eRuntimeError, "Invalid encoding");
}
return parse_nested_query_internal(RSTRING_PTR(r_str), len);
(void)self;
}
|
.rfc2109(rtm) ⇒ Object
Takes time
and returns a faster (though less localized) HTTP Date formatted
String.
Iodine::Rack.rfc2109(Time.now) => "Sun, 11-Jun-2017 06:14:08 GMT"
Iodine::Rack.rfc2109(0) => "Sun, 11-Jun-2017 06:14:08 GMT"
Since Iodine uses time caching within it's reactor, using the default value
(by passing 0) will be faster than providing an explicit time using Time.now
.
198 199 200 201 202 203 204 205 206 207 208 209 210 |
# File 'ext/iodine/iodine_helpers.c', line 198
static VALUE iodine_rfc2109(VALUE self, VALUE rtm) {
time_t last_tick;
rtm = rb_funcallv(rtm, iodine_to_i_func_id, 0, NULL);
last_tick = FIX2ULONG(rtm) ? FIX2ULONG(rtm) : fio_last_tick().tv_sec;
VALUE str = rb_str_buf_new(32);
struct tm tm;
http_gmtime(last_tick, &tm);
size_t len = http_date2rfc2109(RSTRING_PTR(str), &tm);
rb_str_set_len(str, len);
return str;
(void)self;
}
|
.rfc2822(rtm) ⇒ Object
Takes time
and returns a faster (though less localized) HTTP Date formatted
String.
Iodine::Rack.rfc2822(Time.now) => "Sun, 11 Jun 2017 06:14:08 -0000"
Iodine::Rack.rfc2822(0) => "Sun, 11 Jun 2017 06:14:08 -0000"
Since Iodine uses time caching within it's reactor, using the default value
(by passing 0) will be faster than providing an explicit time using Time.now
.
172 173 174 175 176 177 178 179 180 181 182 183 184 |
# File 'ext/iodine/iodine_helpers.c', line 172
static VALUE iodine_rfc2822(VALUE self, VALUE rtm) {
time_t last_tick;
rtm = rb_funcallv(rtm, iodine_to_i_func_id, 0, NULL);
last_tick = FIX2ULONG(rtm) ? FIX2ULONG(rtm) : fio_last_tick().tv_sec;
VALUE str = rb_str_buf_new(34);
struct tm tm;
http_gmtime(last_tick, &tm);
size_t len = http_date2rfc2822(RSTRING_PTR(str), &tm);
rb_str_set_len(str, len);
return str;
(void)self;
}
|
.time2str(*args) ⇒ Object
Takes an optional Integer for Unix Time and returns a faster (though less localized) HTTP Date formatted String.
Iodine::Rack.time2str => "Sun, 11 Jun 2017 06:14:08 GMT"
Iodine::Rack.time2str(Time.now.to_i) => "Wed, 15 Nov 1995 06:25:24 GMT"
Since Iodine uses time caching within it's reactor, using the default value
(now) will be faster than providing an explicit time using Time.now.to_i
.
137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 |
# File 'ext/iodine/iodine_helpers.c', line 137
static VALUE date_str(int argc, VALUE *argv, VALUE self) {
if (argc > 1)
rb_raise(rb_eArgError,
"wrong number of arguments (given %d, expected 0..1).", argc);
time_t last_tick;
if (argc) {
if (TYPE(argv[0]) != T_FIXNUM)
argv[0] = rb_funcallv(argv[0], iodine_to_i_func_id, 0, NULL);
Check_Type(argv[0], T_FIXNUM);
last_tick =
FIX2ULONG(argv[0]) ? FIX2ULONG(argv[0]) : fio_last_tick().tv_sec;
} else
last_tick = fio_last_tick().tv_sec;
VALUE str = rb_str_buf_new(32);
struct tm tm;
http_gmtime(last_tick, &tm);
size_t len = http_date2str(RSTRING_PTR(str), &tm);
rb_str_set_len(str, len);
return str;
(void)self;
}
|