about summary refs log tree commit homepage
path: root/ext
diff options
context:
space:
mode:
authorEric Wong <normalperson@yhbt.net>2010-06-06 05:17:14 +0000
committerEric Wong <normalperson@yhbt.net>2010-06-06 06:26:45 +0000
commitb895d3d0393a647d3602783bc53bf68e223e51c9 (patch)
treedb7a7ed6d0f3200f27617a79aacd402d809eb722 /ext
parentbc1d1df38d7803ce9fdae05fc5129051eeed89e0 (diff)
downloadclogger-b895d3d0393a647d3602783bc53bf68e223e51c9.tar.gz
Certain configurations of Rainbows! (and Zbatery) are able to
use the return value of body.to_path to serve static files
more efficiently.

This also allows middleware like Rack::Contrib::Sendfile to
work properly higher up the stack, too.
Diffstat (limited to 'ext')
-rw-r--r--ext/clogger_ext/clogger.c47
-rw-r--r--ext/clogger_ext/ruby_1_9_compat.h37
2 files changed, 82 insertions, 2 deletions
diff --git a/ext/clogger_ext/clogger.c b/ext/clogger_ext/clogger.c
index 100392d..8392c4c 100644
--- a/ext/clogger_ext/clogger.c
+++ b/ext/clogger_ext/clogger.c
@@ -1,8 +1,14 @@
 #define _BSD_SOURCE
 #include <ruby.h>
+#ifdef HAVE_RUBY_IO_H
+#  include <ruby/io.h>
+#else
+#  include <rubyio.h>
+#endif
 #include <assert.h>
 #include <unistd.h>
 #include <sys/types.h>
+#include <sys/stat.h>
 #include <sys/time.h>
 #include <time.h>
 #include <errno.h>
@@ -89,7 +95,10 @@ static ID to_s_id;
 static ID size_id;
 static ID sq_brace_id;
 static ID new_id;
+static ID to_path_id;
+static ID to_io_id;
 static VALUE cClogger;
+static VALUE cToPath;
 static VALUE mFormat;
 static VALUE cHeaderHash;
 
@@ -638,7 +647,7 @@ static VALUE body_iter_i(VALUE str, VALUE memop)
         return rb_yield(str);
 }
 
-static VALUE wrap_close(struct clogger *c)
+static VALUE body_close(struct clogger *c)
 {
         if (rb_respond_to(c->body, close_id))
                 return rb_funcall(c->body, close_id, 0);
@@ -676,7 +685,7 @@ static VALUE clogger_close(VALUE self)
 {
         struct clogger *c = clogger_get(self);
 
-        return rb_ensure(wrap_close, (VALUE)c, cwrite, (VALUE)c);
+        return rb_ensure(body_close, (VALUE)c, cwrite, (VALUE)c);
 }
 
 /* :nodoc: */
@@ -748,6 +757,9 @@ static VALUE clogger_call(VALUE self, VALUE env)
 
                 rv = ccall(c, env);
                 assert(!OBJ_FROZEN(rv) && "frozen response array");
+
+                if (rb_respond_to(c->body, to_path_id))
+                        self = rb_funcall(cToPath, new_id, 1, self);
                 rb_ary_store(rv, 2, self);
 
                 return rv;
@@ -778,6 +790,33 @@ static VALUE clogger_init_copy(VALUE clone, VALUE orig)
 
 #define CONST_GLOBAL_STR(val) CONST_GLOBAL_STR2(val, #val)
 
+static VALUE to_path(VALUE self)
+{
+        struct clogger *c = clogger_get(RSTRUCT_PTR(self)[0]);
+        VALUE path = rb_funcall(c->body, to_path_id, 0);
+        struct stat sb;
+        int rv;
+        const char *cpath;
+
+        Check_Type(path, T_STRING);
+        cpath = RSTRING_PTR(path);
+
+        /* try to avoid an extra path lookup  */
+        if (rb_respond_to(c->body, to_io_id))
+                rv = fstat(my_fileno(c->body), &sb);
+        /*
+         * Rainbows! can use "/dev/fd/%d" in to_path output to avoid
+         * extra open() syscalls, too.
+         */
+        else if (sscanf(cpath, "/dev/fd/%d", &rv) == 1)
+                rv = fstat(rv, &sb);
+        else
+                rv = stat(cpath, &sb);
+
+        c->body_bytes_sent = rv == 0 ? sb.st_size : 0;
+        return path;
+}
+
 void Init_clogger_ext(void)
 {
         VALUE tmp;
@@ -791,6 +830,8 @@ void Init_clogger_ext(void)
         size_id = rb_intern("size");
         sq_brace_id = rb_intern("[]");
         new_id = rb_intern("new");
+        to_path_id = rb_intern("to_path");
+        to_io_id = rb_intern("to_io");
         cClogger = rb_define_class("Clogger", rb_cObject);
         mFormat = rb_define_module_under(cClogger, "Format");
         rb_define_alloc_func(cClogger, clogger_alloc);
@@ -820,4 +861,6 @@ void Init_clogger_ext(void)
         tmp = rb_const_get(rb_cObject, rb_intern("Rack"));
         tmp = rb_const_get(tmp, rb_intern("Utils"));
         cHeaderHash = rb_const_get(tmp, rb_intern("HeaderHash"));
+        cToPath = rb_const_get(cClogger, rb_intern("ToPath"));
+        rb_define_method(cToPath, "to_path", to_path, 0);
 }
diff --git a/ext/clogger_ext/ruby_1_9_compat.h b/ext/clogger_ext/ruby_1_9_compat.h
index e0ba4ac..b6caa96 100644
--- a/ext/clogger_ext/ruby_1_9_compat.h
+++ b/ext/clogger_ext/ruby_1_9_compat.h
@@ -11,6 +11,12 @@
 #ifndef RARRAY_LEN
 #  define RARRAY_LEN(s) (RARRAY(s)->len)
 #endif
+#ifndef RSTRUCT_PTR
+#  define RSTRUCT_PTR(s) (RSTRUCT(s)->ptr)
+#endif
+#ifndef RSTRUCT_LEN
+#  define RSTRUCT_LEN(s) (RSTRUCT(s)->len)
+#endif
 
 #ifndef HAVE_RB_STR_SET_LEN
 /* this is taken from Ruby 1.8.7, 1.8.6 may not have it */
@@ -21,3 +27,34 @@ static void rb_18_str_set_len(VALUE str, long len)
 }
 #define rb_str_set_len(str,len) rb_18_str_set_len(str,len)
 #endif
+
+#if ! HAVE_RB_IO_T
+#  define rb_io_t OpenFile
+#endif
+
+#ifdef GetReadFile
+#  define FPTR_TO_FD(fptr) (fileno(GetReadFile(fptr)))
+#else
+#  if !HAVE_RB_IO_T || (RUBY_VERSION_MAJOR == 1 && RUBY_VERSION_MINOR == 8)
+#    define FPTR_TO_FD(fptr) fileno(fptr->f)
+#  else
+#    define FPTR_TO_FD(fptr) fptr->fd
+#  endif
+#endif
+
+static int my_fileno(VALUE io)
+{
+        rb_io_t *fptr;
+
+        for (;;) {
+                switch (TYPE(io)) {
+                case T_FILE: {
+                        GetOpenFile(io, fptr);
+                        return FPTR_TO_FD(fptr);
+                }
+                default:
+                        io = rb_convert_type(io, T_FILE, "IO", "to_io");
+                        /* retry */
+                }
+        }
+}