diff options
author | Eric Wong <normalperson@yhbt.net> | 2011-01-04 17:50:51 -0800 |
---|---|---|
committer | Eric Wong <normalperson@yhbt.net> | 2011-01-04 17:51:59 -0800 |
commit | d100025759450dd1cbeccd1a3e44c46921bba26b (patch) | |
tree | 8f623be43ae96cc9246a43b01fb5650751ca3769 /ext | |
parent | 6183611108c571dbed29dfe2854b9f06757fd27f (diff) | |
download | unicorn-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.rb | 1 | ||||
-rw-r--r-- | ext/unicorn_http/httpdate.c | 82 | ||||
-rw-r--r-- | ext/unicorn_http/unicorn_http.rl | 3 |
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 |