diff options
Diffstat (limited to 'ext/clogger_ext/clogger.c')
-rw-r--r-- | ext/clogger_ext/clogger.c | 96 |
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); } |