From b895d3d0393a647d3602783bc53bf68e223e51c9 Mon Sep 17 00:00:00 2001 From: Eric Wong Date: Sun, 6 Jun 2010 05:17:14 +0000 Subject: pass-through body.to_path when wrapping the body 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. --- ext/clogger_ext/clogger.c | 47 +++++++++++++++++++++++++++++++++++++-- ext/clogger_ext/ruby_1_9_compat.h | 37 ++++++++++++++++++++++++++++++ 2 files changed, 82 insertions(+), 2 deletions(-) (limited to 'ext/clogger_ext') 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 +#ifdef HAVE_RUBY_IO_H +# include +#else +# include +#endif #include #include #include +#include #include #include #include @@ -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 */ + } + } +} -- cgit v1.2.3-24-ge0c7