about summary refs log tree commit homepage
path: root/ext/clogger_ext/clogger.c
diff options
context:
space:
mode:
Diffstat (limited to 'ext/clogger_ext/clogger.c')
-rw-r--r--ext/clogger_ext/clogger.c96
1 files changed, 64 insertions, 32 deletions
diff --git a/ext/clogger_ext/clogger.c b/ext/clogger_ext/clogger.c
index 079817c..cea4072 100644
--- a/ext/clogger_ext/clogger.c
+++ b/ext/clogger_ext/clogger.c
@@ -17,7 +17,7 @@
 #  include <fcntl.h>
 #endif
 #ifndef _POSIX_C_SOURCE
-#  define _POSIX_C_SOURCE 200112L
+#  define _POSIX_C_SOURCE 200809L
 #endif
 #include <time.h>
 #include <stdlib.h>
@@ -136,7 +136,7 @@ static ID to_path_id;
 static ID respond_to_id;
 static VALUE cClogger;
 static VALUE mFormat;
-static VALUE cHeaderHash;
+static VALUE cRackHeaders;
 
 /* common hash lookup keys */
 static VALUE g_HTTP_X_FORWARDED_FOR;
@@ -172,6 +172,10 @@ static VALUE byte_xs(VALUE obj)
 {
         static const char esc[] = "0123456789ABCDEF";
         unsigned char *new_ptr;
+        if (rb_obj_is_kind_of(obj, rb_cArray)) {
+                // Rack 3
+                obj = rb_ary_join(obj, rb_str_new2("\n"));
+        }
         VALUE from = rb_obj_as_string(obj);
         const unsigned char *ptr = (const unsigned char *)RSTRING_PTR(from);
         long len = RSTRING_LEN(from);
@@ -225,18 +229,28 @@ static void clogger_mark(void *ptr)
         rb_gc_mark(c->body);
 }
 
+static size_t memsize(const void *ptr)
+{
+        return sizeof(struct clogger);
+}
+
+static const rb_data_type_t clogger_type = {
+        "clogger",
+        { clogger_mark, RUBY_TYPED_DEFAULT_FREE, memsize, /* reserved */ },
+};
+
 static VALUE clogger_alloc(VALUE klass)
 {
         struct clogger *c;
 
-        return Data_Make_Struct(klass, struct clogger, clogger_mark, -1, c);
+        return TypedData_Make_Struct(klass, struct clogger, &clogger_type, c);
 }
 
 static struct clogger *clogger_get(VALUE self)
 {
         struct clogger *c;
 
-        Data_Get_Struct(self, struct clogger, c);
+        TypedData_Get_Struct(self, struct clogger, &clogger_type, c);
         assert(c);
         return c;
 }
@@ -386,30 +400,29 @@ static void append_request_time_fmt(struct clogger *c, VALUE op)
         clock_gettime(hopefully_CLOCK_MONOTONIC, &now);
         clock_diff(&now, &c->ts_start);
         if (ipow) {
-                struct timespec prev;
                 unsigned long adj = 1;
-                /*
-                  * n.b. timespec.tv_sec may not be time_t on some platforms,
-                  * so we use a full timespec struct instead of time_t:
-                  */
-                prev.tv_sec = now.tv_sec;
+                int64_t now_sec = now.tv_sec, now_nsec = now.tv_nsec,
+                        prev_sec = now.tv_sec;
+
                 do { adj *= 10; } while (--ipow);
-                now.tv_sec *= adj;
-                now.tv_nsec *= adj;
-                if (now.tv_nsec >= NANO_PER_SEC) {
-                        int64_t add = now.tv_nsec / NANO_PER_SEC;
-                        now.tv_sec += add;
-                        now.tv_nsec %= NANO_PER_SEC;
+                now_sec *= adj;
+                now_nsec *= adj;
+                if (now_nsec >= NANO_PER_SEC) {
+                        int64_t add = now_nsec / NANO_PER_SEC;
+                        now_sec += add;
+                        now_nsec %= NANO_PER_SEC;
                 }
-                if (now.tv_sec < prev.tv_sec) { /* overflowed */
-                        now.tv_nsec = NANO_PER_SEC - 1;
+                if (now_sec < prev_sec) { /* overflowed */
+                        now_nsec = NANO_PER_SEC - 1;
                         /*
                           * some platforms may use unsigned .tv_sec, but
                           * they're not worth supporting, so keep unsigned:
                           */
-                        now.tv_sec = (time_t)(sizeof(now.tv_sec) == 4 ?
+                        now_sec = (time_t)(sizeof(now.tv_sec) == 4 ?
                                      INT_MAX : LONG_MAX);
                 }
+                now.tv_sec = now_sec;
+                now.tv_nsec = now_nsec;
         }
         append_ts(c, op, &now);
 }
@@ -447,10 +460,11 @@ static void append_request(struct clogger *c)
 {
         VALUE tmp;
 
-        /* REQUEST_METHOD doesn't need escaping, Rack::Lint governs it */
         tmp = rb_hash_aref(c->env, g_REQUEST_METHOD);
-        if (!NIL_P(tmp))
+        if (!NIL_P(tmp)) {
+                tmp = byte_xs(tmp);
                 rb_str_buf_append(c->log_buf, tmp);
+        }
 
         rb_str_buf_append(c->log_buf, g_space);
 
@@ -475,9 +489,21 @@ static void append_request_length(struct clogger *c)
         }
 }
 
+/*
+ * time(2) may slip backwards, so use CLOCK_REALTIME for accuracy
+ * https://lore.kernel.org/git/20230319064353.686226-3-eggert@cs.ucla.edu/T/
+ */
+static time_t cur_time(void)
+{
+        struct timespec now;
+
+        (void)clock_gettime(CLOCK_REALTIME, &now);
+        return now.tv_sec;
+}
+
 static long local_gmtoffset(struct tm *tm)
 {
-        time_t t = time(NULL);
+        time_t t = cur_time();
 
         tzset();
         localtime_r(&t, tm);
@@ -537,7 +563,7 @@ static void append_time_utc(struct clogger *c)
         char buf[sizeof("01/Jan/1970:00:00:00 +0000")];
         struct tm tm;
         int nr;
-        time_t t = time(NULL);
+        time_t t = cur_time();
 
         gmtime_r(&t, &tm);
         nr = snprintf(buf, sizeof(buf),
@@ -556,7 +582,7 @@ append_time(struct clogger *c, enum clogger_opcode op, VALUE fmt, VALUE buf)
         size_t buf_size = RSTRING_LEN(buf) + 1; /* "\0" */
         size_t nr;
         struct tm tmp;
-        time_t t = time(NULL);
+        time_t t = cur_time();
 
         if (op == CL_OP_TIME_LOCAL)
                 localtime_r(&t, &tmp);
@@ -616,7 +642,8 @@ static void append_response(struct clogger *c, VALUE key)
 {
         VALUE v;
 
-        assert(rb_obj_is_kind_of(c->headers, cHeaderHash) && "not HeaderHash");
+        assert(rb_obj_is_kind_of(c->headers, cRackHeaders)
+                && "not Rack::Headers");
 
         v = rb_funcall(c->headers, sq_brace_id, 1, key);
         v = NIL_P(v) ? g_dash : byte_xs(v);
@@ -888,10 +915,9 @@ static VALUE ccall(struct clogger *c, VALUE env)
 
                 rv = rb_ary_dup(rv);
                 if (c->need_resp &&
-                    ! rb_obj_is_kind_of(c->headers, cHeaderHash)) {
-                        c->headers = rb_funcall(cHeaderHash, new_id, 1,
-                                                c->headers);
-                        rb_ary_store(rv, 1, c->headers);
+                    ! rb_obj_is_kind_of(c->headers, cRackHeaders)) {
+                        c->headers = rb_funcall(cRackHeaders, sq_brace_id,
+                                                1, c->headers);
                 }
         } else {
                 VALUE tmp = rb_inspect(rv);
@@ -1102,9 +1128,15 @@ void Init_clogger_ext(void)
         CONST_GLOBAL_STR2(rack_request_cookie_hash, "rack.request.cookie_hash");
 
         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"));
-        rb_ary_push(mark_ary, cHeaderHash);
+        if (rb_const_defined(tmp, rb_intern("Headers"))) {
+                // Rack >= 3.0
+                cRackHeaders = rb_const_get(tmp, rb_intern("Headers"));
+        } else {
+                // Rack < 3.0
+                tmp = rb_const_get(tmp, rb_intern("Utils"));
+                cRackHeaders = rb_const_get(tmp, rb_intern("HeaderHash"));
+        }
+        rb_ary_push(mark_ary, cRackHeaders);
 
         rb_obj_freeze(mark_ary);
 }