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

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;
}

.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) : facil_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) : facil_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]) : facil_last_tick().tv_sec;
  } else
    last_tick = facil_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;
}