about summary refs log tree commit homepage
path: root/ext
diff options
context:
space:
mode:
authorEric Wong <normalperson@yhbt.net>2011-01-04 17:50:51 -0800
committerEric Wong <normalperson@yhbt.net>2011-01-04 17:51:59 -0800
commitd100025759450dd1cbeccd1a3e44c46921bba26b (patch)
tree8f623be43ae96cc9246a43b01fb5650751ca3769 /ext
parent6183611108c571dbed29dfe2854b9f06757fd27f (diff)
downloadunicorn-d100025759450dd1cbeccd1a3e44c46921bba26b.tar.gz
This can return a static string and be significantly
faster as it reduces object allocations and Ruby method
calls for the fastest websites that serve thousands of
requests a second.

It assumes the Ruby runtime is single-threaded, but that
is the case of Ruby 1.8 and 1.9 and also what Unicorn
is all about.  This change is safe for Rainbows! under 1.8
and 1.9.
Diffstat (limited to 'ext')
-rw-r--r--ext/unicorn_http/extconf.rb1
-rw-r--r--ext/unicorn_http/httpdate.c82
-rw-r--r--ext/unicorn_http/unicorn_http.rl3
3 files changed, 86 insertions, 0 deletions
diff --git a/ext/unicorn_http/extconf.rb b/ext/unicorn_http/extconf.rb
index a2c8442..7da82e7 100644
--- a/ext/unicorn_http/extconf.rb
+++ b/ext/unicorn_http/extconf.rb
@@ -4,5 +4,6 @@ require 'mkmf'
 have_macro("SIZEOF_OFF_T", "ruby.h") or check_sizeof("off_t", "sys/types.h")
 have_macro("SIZEOF_LONG", "ruby.h") or check_sizeof("long", "sys/types.h")
 have_func("rb_str_set_len", "ruby.h")
+have_func("gmtime_r", "time.h")
 
 create_makefile("unicorn_http")
diff --git a/ext/unicorn_http/httpdate.c b/ext/unicorn_http/httpdate.c
new file mode 100644
index 0000000..bfa11ca
--- /dev/null
+++ b/ext/unicorn_http/httpdate.c
@@ -0,0 +1,82 @@
+#include <ruby.h>
+#include <time.h>
+#include <stdio.h>
+
+static const size_t buf_capa = sizeof("Thu, 01 Jan 1970 00:00:00 GMT");
+static VALUE buf;
+static char *buf_ptr;
+static const char *const week[] = {
+        "Sun", "Mon", "Tue", "Wed", "Thu", "Fri", "Sat"
+};
+static const char *const months[] = {
+        "Jan", "Feb", "Mar", "Apr", "May", "Jun",
+        "Jul", "Aug", "Sep", "Oct", "Nov", "Dec"
+};
+
+/* for people on wonky systems only */
+#ifndef HAVE_GMTIME_R
+static struct tm * my_gmtime_r(time_t *now, struct tm *tm)
+{
+        struct tm *global = gmtime(now);
+        if (global)
+                *tm = *global;
+        return tm;
+}
+#  define gmtime_r my_gmtime_r
+#endif
+
+
+/*
+ * Returns a string which represents the time as rfc1123-date of HTTP-date
+ * defined by RFC 2616:
+ *
+ *   day-of-week, DD month-name CCYY hh:mm:ss GMT
+ *
+ * Note that the result is always GMT.
+ *
+ * This method is identical to Time#httpdate in the Ruby standard library,
+ * except it is implemented in C for performance.  We always saw
+ * Time#httpdate at or near the top of the profiler output so we
+ * decided to rewrite this in C.
+ *
+ * Caveats: it relies on a Ruby implementation with the global VM lock,
+ * a thread-safe version will be provided when a Unix-only, GVL-free Ruby
+ * implementation becomes viable.
+ */
+static VALUE httpdate(VALUE self)
+{
+        static time_t last;
+        time_t now = time(NULL); /* not a syscall on modern 64-bit systems */
+        struct tm tm;
+
+        if (last == now)
+                return buf;
+        last = now;
+        gmtime_r(&now, &tm);
+
+        /* we can make this thread-safe later if our Ruby loses the GVL */
+        snprintf(buf_ptr, buf_capa,
+                 "%s, %02d %s %4d %02d:%02d:%02d GMT",
+                 week[tm.tm_wday],
+                 tm.tm_mday,
+                 months[tm.tm_mon],
+                 tm.tm_year + 1900,
+                 tm.tm_hour,
+                 tm.tm_min,
+                 tm.tm_sec);
+
+        return buf;
+}
+
+void init_unicorn_httpdate(void)
+{
+        VALUE mod = rb_const_get(rb_cObject, rb_intern("Unicorn"));
+        mod = rb_define_module_under(mod, "HttpResponse");
+
+        buf = rb_str_new(0, buf_capa - 1);
+        rb_global_variable(&buf);
+        buf_ptr = RSTRING_PTR(buf);
+        httpdate(Qnil);
+
+        rb_define_method(mod, "httpdate", httpdate, 0);
+}
diff --git a/ext/unicorn_http/unicorn_http.rl b/ext/unicorn_http/unicorn_http.rl
index 9b33e31..fd259b6 100644
--- a/ext/unicorn_http/unicorn_http.rl
+++ b/ext/unicorn_http/unicorn_http.rl
@@ -12,6 +12,8 @@
 #include "global_variables.h"
 #include "c_util.h"
 
+void init_unicorn_httpdate(void);
+
 #define UH_FL_CHUNKED  0x1
 #define UH_FL_HASBODY  0x2
 #define UH_FL_INBODY   0x4
@@ -897,5 +899,6 @@ void Init_unicorn_http(void)
   SET_GLOBAL(g_content_length, "CONTENT_LENGTH");
   SET_GLOBAL(g_http_connection, "CONNECTION");
   id_clear = rb_intern("clear");
+  init_unicorn_httpdate();
 }
 #undef SET_GLOBAL