From 916e7f09c56ff0ab6722c4cce5e78d71ec336d48 Mon Sep 17 00:00:00 2001 From: Eric Wong Date: Fri, 14 Jan 2011 16:20:40 -0800 Subject: handle abitrarily long time formats in C extension In case some folks need to use insanely long time formats, we'll support them. --- ext/clogger_ext/clogger.c | 35 ++++++++++++++++++++++++++--------- lib/clogger.rb | 7 +++++-- test/test_clogger.rb | 14 +++++++++++++- 3 files changed, 44 insertions(+), 12 deletions(-) diff --git a/ext/clogger_ext/clogger.c b/ext/clogger_ext/clogger.c index ac473bf..253339f 100644 --- a/ext/clogger_ext/clogger.c +++ b/ext/clogger_ext/clogger.c @@ -442,10 +442,11 @@ static void append_request_length(struct clogger *c) } } -static void append_time(struct clogger *c, enum clogger_opcode op, VALUE fmt) +static void +append_time(struct clogger *c, enum clogger_opcode op, VALUE fmt, VALUE buf) { - /* you'd have to be a moron to use formats this big... */ - char buf[sizeof("Saturday, November 01, 1970, 00:00:00 PM +0000")]; + char *buf_ptr = RSTRING_PTR(buf); + size_t buf_size = RSTRING_LEN(buf) + 1; /* "\0" */ size_t nr; struct tm tmp; time_t t = time(NULL); @@ -457,11 +458,9 @@ static void append_time(struct clogger *c, enum clogger_opcode op, VALUE fmt) else assert(0 && "unknown op"); - nr = strftime(buf, sizeof(buf), RSTRING_PTR(fmt), &tmp); - if (nr == 0 || nr == sizeof(buf)) - rb_str_buf_append(c->log_buf, g_dash); - else - rb_str_buf_cat(c->log_buf, buf, nr); + nr = strftime(buf_ptr, buf_size, RSTRING_PTR(fmt), &tmp); + assert(nr < buf_size && "time format too small!"); + rb_str_buf_cat(c->log_buf, buf_ptr, nr); } static void append_pid(struct clogger *c) @@ -581,7 +580,7 @@ static VALUE cwrite(struct clogger *c) break; case CL_OP_TIME_LOCAL: case CL_OP_TIME_UTC: - append_time(c, opcode, op[1]); + append_time(c, opcode, op[1], op[2]); break; case CL_OP_REQUEST_TIME: append_request_time_fmt(c, op); @@ -818,6 +817,23 @@ static VALUE clogger_call(VALUE self, VALUE env) return rv; } +static void duplicate_buffers(VALUE ops) +{ + long i = RARRAY_LEN(ops); + VALUE *ary = RARRAY_PTR(ops); + + for ( ; --i >= 0; ary++) { + VALUE *op = RARRAY_PTR(*ary); + enum clogger_opcode opcode = FIX2INT(op[0]); + + if (opcode == CL_OP_TIME_LOCAL || opcode == CL_OP_TIME_UTC) { + Check_Type(op[2], T_STRING); + op[2] = rb_str_dup(op[2]); + rb_str_modify(op[2]); /* trigger copy-on-write */ + } + } +} + /* :nodoc: */ static VALUE clogger_init_copy(VALUE clone, VALUE orig) { @@ -826,6 +842,7 @@ static VALUE clogger_init_copy(VALUE clone, VALUE orig) memcpy(b, a, sizeof(struct clogger)); init_buffers(b); + duplicate_buffers(b->fmt_ops); return clone; } diff --git a/lib/clogger.rb b/lib/clogger.rb index a64ca09..75a639c 100644 --- a/lib/clogger.rb +++ b/lib/clogger.rb @@ -57,6 +57,7 @@ private \w*))?([^$]*)/x def compile_format(str, opt = {}) + longest_day = Time.at(26265600) # "Saturday, November 01, 1970 00:00:00" rv = [] opt ||= {} str.scan(SCAN).each do |pre,tok,post| @@ -83,9 +84,11 @@ private when /\A\$sent_http_(\w+)\z/ rv << [ OP_RESPONSE, $1.downcase.tr('_','-') ] when /\A\$time_local\{([^\}]+)\}\z/ - rv << [ OP_TIME_LOCAL, $1 ] + fmt = $1 + rv << [ OP_TIME_LOCAL, fmt, longest_day.strftime(fmt) ] when /\A\$time_utc\{([^\}]+)\}\z/ - rv << [ OP_TIME_UTC, $1 ] + fmt = $1 + rv << [ OP_TIME_UTC, fmt, longest_day.strftime(fmt) ] when /\A\$time\{(\d+)\}\z/ rv << [ OP_TIME, *usec_conv_pair(tok, $1.to_i) ] when /\A\$request_time\{(\d+)\}\z/ diff --git a/test/test_clogger.rb b/test/test_clogger.rb index 58d38e8..2cd895c 100644 --- a/test/test_clogger.rb +++ b/test/test_clogger.rb @@ -156,12 +156,13 @@ class TestClogger < Test::Unit::TestCase '$env{rack.url_scheme}' \ "\n") } + longest_day = Time.at(26265600).strftime('%d/%b/%Y:%H:%M:%S %z') expect = [ [ Clogger::OP_REQUEST, "REMOTE_ADDR" ], [ Clogger::OP_LITERAL, " - " ], [ Clogger::OP_REQUEST, "REMOTE_USER" ], [ Clogger::OP_LITERAL, " [" ], - [ Clogger::OP_TIME_LOCAL, '%d/%b/%Y:%H:%M:%S %z' ], + [ Clogger::OP_TIME_LOCAL, '%d/%b/%Y:%H:%M:%S %z', longest_day ], [ Clogger::OP_LITERAL, "] \"" ], [ Clogger::OP_SPECIAL, Clogger::SPECIAL_VARS[:request] ], [ Clogger::OP_LITERAL, "\" "], @@ -677,4 +678,15 @@ class TestClogger < Test::Unit::TestCase assert s[-1].to_f >= 0.100 assert s[-1].to_f <= 0.110 end + + def test_insanely_long_time_format + s = [] + app = lambda { |env| [200, [], [] ] } + fmt = '%Y' * 100 + expect = Time.now.utc.strftime(fmt) << "\n" + assert_equal 100 * 4 + 1, expect.size + cl = Clogger.new(app, :logger => s, :format => "$time_utc{#{fmt}}") + status, headers, body = cl.call(@req) + assert_equal expect, s[0] + end end -- cgit v1.2.3-24-ge0c7