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