about summary refs log tree commit homepage
diff options
context:
space:
mode:
-rw-r--r--ext/clogger_ext/clogger.c35
-rw-r--r--lib/clogger.rb7
-rw-r--r--test/test_clogger.rb14
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