ext: compiler compatibility fix
[clogger.git] / ext / clogger_ext / clogger.c
blob369baf6f376cfa90ecde73814e68567802e39290
1 #define _BSD_SOURCE
2 #include <ruby.h>
3 #include <assert.h>
4 #include <unistd.h>
5 #include <sys/types.h>
6 #include <sys/time.h>
7 #include <time.h>
8 #include <errno.h>
9 #ifdef HAVE_FCNTL_H
10 # include <fcntl.h>
11 #endif
12 #include "ruby_1_9_compat.h"
14 /* in case _BSD_SOURCE doesn't give us this macro */
15 #ifndef timersub
16 # define timersub(a, b, result) \
17 do { \
18 (result)->tv_sec = (a)->tv_sec - (b)->tv_sec; \
19 (result)->tv_usec = (a)->tv_usec - (b)->tv_usec; \
20 if ((result)->tv_usec < 0) { \
21 --(result)->tv_sec; \
22 (result)->tv_usec += 1000000; \
23 } \
24 } while (0)
25 #endif
27 /* give GCC hints for better branch prediction
28 * (we layout branches so that ASCII characters are handled faster) */
29 #if defined(__GNUC__) && (__GNUC__ >= 3)
30 # define likely(x) __builtin_expect (!!(x), 1)
31 # define unlikely(x) __builtin_expect (!!(x), 0)
32 #else
33 # define unlikely(x) (x)
34 # define likely(x) (x)
35 #endif
37 enum clogger_opcode {
38 CL_OP_LITERAL = 0,
39 CL_OP_REQUEST,
40 CL_OP_RESPONSE,
41 CL_OP_SPECIAL,
42 CL_OP_EVAL,
43 CL_OP_TIME_LOCAL,
44 CL_OP_TIME_UTC,
45 CL_OP_REQUEST_TIME,
46 CL_OP_TIME,
47 CL_OP_COOKIE
50 enum clogger_special {
51 CL_SP_body_bytes_sent = 0,
52 CL_SP_status,
53 CL_SP_request,
54 CL_SP_request_length,
55 CL_SP_response_length,
56 CL_SP_ip,
57 CL_SP_pid,
58 CL_SP_request_uri
61 struct clogger {
62 VALUE app;
64 VALUE fmt_ops;
65 VALUE logger;
66 VALUE log_buf;
68 VALUE env;
69 VALUE cookies;
70 VALUE response;
72 off_t body_bytes_sent;
73 struct timeval tv_start;
75 int fd;
76 int wrap_body;
77 int need_resp;
78 int reentrant; /* tri-state, -1:auto, 1/0 true/false */
81 static ID ltlt_id;
82 static ID call_id;
83 static ID each_id;
84 static ID close_id;
85 static ID to_i_id;
86 static ID to_s_id;
87 static ID size_id;
88 static VALUE cClogger;
89 static VALUE mFormat;
91 /* common hash lookup keys */
92 static VALUE g_HTTP_X_FORWARDED_FOR;
93 static VALUE g_REMOTE_ADDR;
94 static VALUE g_REQUEST_METHOD;
95 static VALUE g_PATH_INFO;
96 static VALUE g_REQUEST_URI;
97 static VALUE g_QUERY_STRING;
98 static VALUE g_HTTP_VERSION;
99 static VALUE g_rack_errors;
100 static VALUE g_rack_input;
101 static VALUE g_rack_multithread;
102 static VALUE g_dash;
103 static VALUE g_empty;
104 static VALUE g_space;
105 static VALUE g_question_mark;
106 static VALUE g_rack_request_cookie_hash;
107 static VALUE g_bad_app_response;
109 #define LOG_BUF_INIT_SIZE 128
111 static void init_buffers(struct clogger *c)
113 c->log_buf = rb_str_buf_new(LOG_BUF_INIT_SIZE);
116 static inline int need_escape(unsigned c)
118 assert(c <= 0xff);
119 return !!(c == '\'' || c == '"' || (c >= 0 && c <= 0x1f));
122 /* we are encoding-agnostic, clients can send us all sorts of junk */
123 static VALUE byte_xs(VALUE from)
125 static const char esc[] = "0123456789ABCDEF";
126 unsigned char *new_ptr;
127 unsigned char *ptr = (unsigned char *)RSTRING_PTR(from);
128 long len = RSTRING_LEN(from);
129 long new_len = len;
130 VALUE rv;
132 for (; --len >= 0; ptr++) {
133 unsigned c = *ptr;
135 if (unlikely(need_escape(c)))
136 new_len += 3; /* { '\', 'x', 'X', 'X' } */
139 len = RSTRING_LEN(from);
140 if (new_len == len)
141 return from;
143 rv = rb_str_new(0, new_len);
144 new_ptr = (unsigned char *)RSTRING_PTR(rv);
145 ptr = (unsigned char *)RSTRING_PTR(from);
146 for (; --len >= 0; ptr++) {
147 unsigned c = *ptr;
149 if (unlikely(need_escape(c))) {
150 *new_ptr++ = '\\';
151 *new_ptr++ = 'x';
152 *new_ptr++ = esc[c >> 4];
153 *new_ptr++ = esc[c & 0xf];
154 } else {
155 *new_ptr++ = c;
158 assert(RSTRING_PTR(rv)[RSTRING_LEN(rv)] == '\0');
160 return rv;
163 /* strcasecmp isn't locale independent, so we roll our own */
164 static int str_case_eq(VALUE a, VALUE b)
166 long alen = RSTRING_LEN(a);
167 long blen = RSTRING_LEN(b);
169 if (alen == blen) {
170 const char *aptr = RSTRING_PTR(a);
171 const char *bptr = RSTRING_PTR(b);
173 for (; alen--; ++aptr, ++bptr) {
174 if ((*bptr == *aptr)
175 || (*aptr >= 'A' && *aptr <= 'Z' &&
176 (*aptr | 0x20) == *bptr))
177 continue;
178 return 0;
180 return 1;
182 return 0;
185 struct response_ops { long nr; VALUE ops; };
187 /* this can be worse than O(M*N) :<... but C loops are fast ... */
188 static VALUE swap_sent_headers(VALUE kv, VALUE memo)
190 struct response_ops *tmp = (struct response_ops *)memo;
191 VALUE key = rb_obj_as_string(RARRAY_PTR(kv)[0]);
192 long i = RARRAY_LEN(tmp->ops);
193 VALUE *ary = RARRAY_PTR(tmp->ops);
194 VALUE value;
196 for (; --i >= 0; ary++) {
197 VALUE *op = RARRAY_PTR(*ary);
198 enum clogger_opcode opcode = NUM2INT(op[0]);
200 if (opcode != CL_OP_RESPONSE)
201 continue;
202 assert(RARRAY_LEN(*ary) == 2);
203 if (!str_case_eq(key, op[1]))
204 continue;
206 value = RARRAY_PTR(kv)[1];
207 op[0] = INT2NUM(CL_OP_LITERAL);
208 op[1] = byte_xs(rb_obj_as_string(value));
210 if (!--tmp->nr)
211 rb_iter_break();
212 return Qnil;
214 return Qnil;
217 static VALUE sent_headers_ops(struct clogger *c)
219 struct response_ops tmp;
220 long i, len;
221 VALUE *ary;
223 if (!c->need_resp)
224 return c->fmt_ops;
226 tmp.nr = 0;
227 tmp.ops = rb_ary_dup(c->fmt_ops);
228 len = RARRAY_LEN(tmp.ops);
229 ary = RARRAY_PTR(tmp.ops);
231 for (i = 0; i < len; ++i) {
232 VALUE *op = RARRAY_PTR(ary[i]);
234 if (NUM2INT(op[0]) == CL_OP_RESPONSE) {
235 assert(RARRAY_LEN(ary[i]) == 2);
236 ary[i] = rb_ary_dup(ary[i]);
237 ++tmp.nr;
241 rb_iterate(rb_each, RARRAY_PTR(c->response)[1],
242 swap_sent_headers, (VALUE)&tmp);
244 return tmp.ops;
247 static void clogger_mark(void *ptr)
249 struct clogger *c = ptr;
251 rb_gc_mark(c->app);
252 rb_gc_mark(c->fmt_ops);
253 rb_gc_mark(c->logger);
254 rb_gc_mark(c->log_buf);
255 rb_gc_mark(c->env);
256 rb_gc_mark(c->cookies);
257 rb_gc_mark(c->response);
260 static VALUE clogger_alloc(VALUE klass)
262 struct clogger *c;
264 return Data_Make_Struct(klass, struct clogger, clogger_mark, 0, c);
267 static struct clogger *clogger_get(VALUE self)
269 struct clogger *c;
271 Data_Get_Struct(self, struct clogger, c);
272 assert(c);
273 return c;
276 static VALUE obj_fileno(VALUE obj)
278 return rb_funcall(obj, rb_intern("fileno"), 0);
281 /* only for writing to regular files, not stupid crap like NFS */
282 static void write_full(int fd, const void *buf, size_t count)
284 ssize_t r;
286 while (count > 0) {
287 r = write(fd, buf, count);
289 if (r == count) { /* overwhelmingly likely */
290 return;
291 } else if (r > 0) {
292 count -= r;
293 buf += r;
294 } else {
295 if (errno == EINTR || errno == EAGAIN)
296 continue; /* poor souls on NFS and like: */
297 if (!errno)
298 errno = ENOSPC;
299 rb_sys_fail("write");
305 * allow us to use write_full() iff we detect a blocking file
306 * descriptor that wouldn't play nicely with Ruby threading/fibers
308 static int raw_fd(VALUE fileno)
310 #if defined(HAVE_FCNTL) && defined(F_GETFL) && defined(O_NONBLOCK)
311 int fd;
312 int flags;
314 if (NIL_P(fileno))
315 return -1;
316 fd = NUM2INT(fileno);
318 flags = fcntl(fd, F_GETFL);
319 if (flags < 0)
320 rb_sys_fail("fcntl");
322 return (flags & O_NONBLOCK) ? -1 : fd;
323 #else /* platforms w/o fcntl/F_GETFL/O_NONBLOCK */
324 return -1;
325 #endif /* platforms w/o fcntl/F_GETFL/O_NONBLOCK */
328 /* :nodoc: */
329 static VALUE clogger_reentrant(VALUE self)
331 return clogger_get(self)->reentrant == 0 ? Qfalse : Qtrue;
334 /* :nodoc: */
335 static VALUE clogger_wrap_body(VALUE self)
337 return clogger_get(self)->wrap_body == 0 ? Qfalse : Qtrue;
340 static void append_status(struct clogger *c)
342 char buf[sizeof("999")];
343 int nr;
344 VALUE status = RARRAY_PTR(c->response)[0];
346 if (TYPE(status) != T_FIXNUM) {
347 status = rb_funcall(status, to_i_id, 0);
348 /* no way it's a valid status code (at least not HTTP/1.1) */
349 if (TYPE(status) != T_FIXNUM) {
350 rb_str_buf_append(c->log_buf, g_dash);
351 return;
355 nr = NUM2INT(status);
356 if (nr >= 100 && nr <= 999) {
357 nr = snprintf(buf, sizeof(buf), "%03d", nr);
358 assert(nr == 3);
359 rb_str_buf_cat(c->log_buf, buf, nr);
360 } else {
361 /* raise?, swap for 500? */
362 rb_str_buf_append(c->log_buf, g_dash);
366 /* this is Rack 1.0.0-compatible, won't try to parse commas in XFF */
367 static void append_ip(struct clogger *c)
369 VALUE env = c->env;
370 VALUE tmp = rb_hash_aref(env, g_HTTP_X_FORWARDED_FOR);
372 if (NIL_P(tmp)) {
373 /* can't be faked on any real server, so no escape */
374 tmp = rb_hash_aref(env, g_REMOTE_ADDR);
375 if (NIL_P(tmp))
376 tmp = g_dash;
377 } else {
378 tmp = byte_xs(tmp);
380 rb_str_buf_append(c->log_buf, tmp);
383 static void append_body_bytes_sent(struct clogger *c)
385 char buf[(sizeof(off_t) * 8) / 3 + 1];
386 const char *fmt = sizeof(off_t) == sizeof(long) ? "%ld" : "%lld";
387 int nr = snprintf(buf, sizeof(buf), fmt, c->body_bytes_sent);
389 assert(nr > 0 && nr < sizeof(buf));
390 rb_str_buf_cat(c->log_buf, buf, nr);
393 static void append_tv(struct clogger *c, const VALUE *op, struct timeval *tv)
395 char buf[sizeof(".000000") + ((sizeof(tv->tv_sec) * 8) / 3)];
396 int nr;
397 char *fmt = RSTRING_PTR(op[1]);
398 int div = NUM2INT(op[2]);
400 nr = snprintf(buf, sizeof(buf), fmt,
401 (int)tv->tv_sec, (int)(tv->tv_usec / div));
402 assert(nr > 0 && nr < sizeof(buf));
403 rb_str_buf_cat(c->log_buf, buf, nr);
406 static void append_request_time_fmt(struct clogger *c, const VALUE *op)
408 struct timeval now, d;
410 gettimeofday(&now, NULL);
411 timersub(&now, &c->tv_start, &d);
412 append_tv(c, op, &d);
415 static void append_time_fmt(struct clogger *c, const VALUE *op)
417 struct timeval now;
419 gettimeofday(&now, NULL);
420 append_tv(c, op, &now);
423 static void append_request_uri(struct clogger *c)
425 VALUE tmp;
427 tmp = rb_hash_aref(c->env, g_REQUEST_URI);
428 if (NIL_P(tmp)) {
429 tmp = rb_hash_aref(c->env, g_PATH_INFO);
430 if (!NIL_P(tmp))
431 rb_str_buf_append(c->log_buf, byte_xs(tmp));
432 tmp = rb_hash_aref(c->env, g_QUERY_STRING);
433 if (!NIL_P(tmp) && RSTRING_LEN(tmp) != 0) {
434 rb_str_buf_append(c->log_buf, g_question_mark);
435 rb_str_buf_append(c->log_buf, byte_xs(tmp));
437 } else {
438 rb_str_buf_append(c->log_buf, byte_xs(tmp));
442 static void append_request(struct clogger *c)
444 VALUE tmp;
446 /* REQUEST_METHOD doesn't need escaping, Rack::Lint governs it */
447 tmp = rb_hash_aref(c->env, g_REQUEST_METHOD);
448 if (!NIL_P(tmp))
449 rb_str_buf_append(c->log_buf, tmp);
451 rb_str_buf_append(c->log_buf, g_space);
453 append_request_uri(c);
455 rb_str_buf_append(c->log_buf, g_space);
457 /* HTTP_VERSION can be injected by malicious clients */
458 tmp = rb_hash_aref(c->env, g_HTTP_VERSION);
459 if (!NIL_P(tmp))
460 rb_str_buf_append(c->log_buf, byte_xs(tmp));
463 static void append_request_length(struct clogger *c)
465 VALUE tmp = rb_hash_aref(c->env, g_rack_input);
466 if (NIL_P(tmp)) {
467 rb_str_buf_append(c->log_buf, g_dash);
468 } else {
469 tmp = rb_funcall(tmp, size_id, 0);
470 rb_str_buf_append(c->log_buf, rb_funcall(tmp, to_s_id, 0));
474 static void append_time(struct clogger *c, enum clogger_opcode op, VALUE fmt)
476 /* you'd have to be a moron to use formats this big... */
477 char buf[sizeof("Saturday, November 01, 1970, 00:00:00 PM +0000")];
478 size_t nr;
479 struct tm tmp;
480 time_t t = time(NULL);
482 if (op == CL_OP_TIME_LOCAL)
483 localtime_r(&t, &tmp);
484 else if (op == CL_OP_TIME_UTC)
485 gmtime_r(&t, &tmp);
486 else
487 assert(0 && "unknown op");
489 nr = strftime(buf, sizeof(buf), RSTRING_PTR(fmt), &tmp);
490 if (nr == 0 || nr == sizeof(buf))
491 rb_str_buf_append(c->log_buf, g_dash);
492 else
493 rb_str_buf_cat(c->log_buf, buf, nr);
496 static void append_pid(struct clogger *c)
498 char buf[(sizeof(pid_t) * 8) / 3 + 1];
499 int nr = snprintf(buf, sizeof(buf), "%d", (int)getpid());
501 assert(nr > 0 && nr < sizeof(buf));
502 rb_str_buf_cat(c->log_buf, buf, nr);
505 static void append_eval(struct clogger *c, VALUE str)
507 int state = -1;
508 VALUE rv = rb_eval_string_protect(RSTRING_PTR(str), &state);
510 rv = state == 0 ? rb_obj_as_string(rv) : g_dash;
511 rb_str_buf_append(c->log_buf, rv);
514 static void append_cookie(struct clogger *c, VALUE key)
516 VALUE cookie;
518 if (c->cookies == Qfalse)
519 c->cookies = rb_hash_aref(c->env, g_rack_request_cookie_hash);
521 if (NIL_P(c->cookies)) {
522 cookie = g_dash;
523 } else {
524 cookie = rb_hash_aref(c->cookies, key);
525 if (NIL_P(cookie))
526 cookie = g_dash;
528 rb_str_buf_append(c->log_buf, cookie);
531 static void append_request_env(struct clogger *c, VALUE key)
533 VALUE tmp = rb_hash_aref(c->env, key);
535 tmp = NIL_P(tmp) ? g_dash : byte_xs(rb_obj_as_string(tmp));
536 rb_str_buf_append(c->log_buf, tmp);
539 static void special_var(struct clogger *c, enum clogger_special var)
541 switch (var) {
542 case CL_SP_body_bytes_sent:
543 append_body_bytes_sent(c);
544 break;
545 case CL_SP_status:
546 append_status(c);
547 break;
548 case CL_SP_request:
549 append_request(c);
550 break;
551 case CL_SP_request_length:
552 append_request_length(c);
553 break;
554 case CL_SP_response_length:
555 if (c->body_bytes_sent == 0)
556 rb_str_buf_append(c->log_buf, g_dash);
557 else
558 append_body_bytes_sent(c);
559 break;
560 case CL_SP_ip:
561 append_ip(c);
562 break;
563 case CL_SP_pid:
564 append_pid(c);
565 break;
566 case CL_SP_request_uri:
567 append_request_uri(c);
571 static VALUE cwrite(struct clogger *c)
573 const VALUE ops = sent_headers_ops(c);
574 const VALUE *ary = RARRAY_PTR(ops);
575 long i = RARRAY_LEN(ops);
576 VALUE dst = c->log_buf;
578 rb_str_set_len(dst, 0);
580 for (; --i >= 0; ary++) {
581 const VALUE *op = RARRAY_PTR(*ary);
582 enum clogger_opcode opcode = NUM2INT(op[0]);
584 switch (opcode) {
585 case CL_OP_LITERAL:
586 rb_str_buf_append(dst, op[1]);
587 break;
588 case CL_OP_REQUEST:
589 append_request_env(c, op[1]);
590 break;
591 case CL_OP_RESPONSE:
592 /* headers we found already got swapped for literals */
593 rb_str_buf_append(dst, g_dash);
594 break;
595 case CL_OP_SPECIAL:
596 special_var(c, NUM2INT(op[1]));
597 break;
598 case CL_OP_EVAL:
599 append_eval(c, op[1]);
600 break;
601 case CL_OP_TIME_LOCAL:
602 case CL_OP_TIME_UTC:
603 append_time(c, opcode, op[1]);
604 break;
605 case CL_OP_REQUEST_TIME:
606 append_request_time_fmt(c, op);
607 break;
608 case CL_OP_TIME:
609 append_time_fmt(c, op);
610 break;
611 case CL_OP_COOKIE:
612 append_cookie(c, op[1]);
613 break;
617 if (c->fd >= 0) {
618 write_full(c->fd, RSTRING_PTR(dst), RSTRING_LEN(dst));
619 } else {
620 VALUE logger = c->logger;
622 if (NIL_P(logger))
623 logger = rb_hash_aref(c->env, g_rack_errors);
624 rb_funcall(logger, ltlt_id, 1, dst);
627 return Qnil;
631 * call-seq:
632 * Clogger.new(app, :logger => $stderr, :format => string) => obj
634 * Creates a new Clogger object that wraps +app+. +:logger+ may
635 * be any object that responds to the "<<" method with a string argument.
637 static VALUE clogger_init(int argc, VALUE *argv, VALUE self)
639 struct clogger *c = clogger_get(self);
640 VALUE o = Qnil;
641 VALUE fmt = rb_const_get(mFormat, rb_intern("Common"));
643 rb_scan_args(argc, argv, "11", &c->app, &o);
644 c->fd = -1;
645 c->logger = Qnil;
646 c->reentrant = -1; /* auto-detect */
648 if (TYPE(o) == T_HASH) {
649 VALUE tmp;
651 c->logger = rb_hash_aref(o, ID2SYM(rb_intern("logger")));
652 if (!NIL_P(c->logger))
653 c->fd = raw_fd(rb_rescue(obj_fileno, c->logger, 0, 0));
655 tmp = rb_hash_aref(o, ID2SYM(rb_intern("format")));
656 if (!NIL_P(tmp))
657 fmt = tmp;
660 init_buffers(c);
661 c->fmt_ops = rb_funcall(self, rb_intern("compile_format"), 1, fmt);
663 if (Qtrue == rb_funcall(self, rb_intern("need_response_headers?"),
664 1, c->fmt_ops))
665 c->need_resp = 1;
666 if (Qtrue == rb_funcall(self, rb_intern("need_wrap_body?"),
667 1, c->fmt_ops))
668 c->wrap_body = 1;
670 return self;
673 static VALUE body_iter_i(VALUE str, VALUE memop)
675 off_t *len = (off_t *)memop;
677 str = rb_obj_as_string(str);
678 *len += RSTRING_LEN(str);
680 return rb_yield(str);
683 static VALUE wrap_each(struct clogger *c)
685 VALUE body = RARRAY_PTR(c->response)[2];
687 c->body_bytes_sent = 0;
688 rb_iterate(rb_each, body, body_iter_i, (VALUE)&c->body_bytes_sent);
690 return body;
694 * call-seq:
695 * clogger.each { |part| socket.write(part) }
697 * Delegates the body#each call to the underlying +body+ object
698 * while tracking the number of bytes yielded. This will log
699 * the request.
701 static VALUE clogger_each(VALUE self)
703 struct clogger *c = clogger_get(self);
705 rb_need_block();
707 return rb_ensure(wrap_each, (VALUE)c, cwrite, (VALUE)c);
711 * call-seq:
712 * clogger.close
714 * Delegates the body#close call to the underlying +body+ object.
715 * This is only used when Clogger is wrapping the +body+ of a Rack
716 * response and should be automatically called by the web server.
718 static VALUE clogger_close(VALUE self)
720 struct clogger *c = clogger_get(self);
722 return rb_funcall(RARRAY_PTR(c->response)[2], close_id, 0);
725 /* :nodoc: */
726 static VALUE clogger_fileno(VALUE self)
728 struct clogger *c = clogger_get(self);
730 return c->fd < 0 ? Qnil : INT2NUM(c->fd);
733 static void ccall(struct clogger *c, VALUE env)
735 VALUE rv;
737 gettimeofday(&c->tv_start, NULL);
738 c->env = env;
739 c->cookies = Qfalse;
740 rv = rb_funcall(c->app, call_id, 1, env);
741 if (TYPE(rv) == T_ARRAY && RARRAY_LEN(rv) == 3) {
742 c->response = rv;
743 } else {
744 c->response = g_bad_app_response;
745 cwrite(c);
746 rb_raise(rb_eTypeError,
747 "app response not a 3 element Array: %s",
748 RSTRING_PTR(rb_inspect(rv)));
753 * call-seq:
754 * clogger.call(env) => [ status, headers, body ]
756 * calls the wrapped Rack application with +env+, returns the
757 * [status, headers, body ] tuplet required by Rack.
759 static VALUE clogger_call(VALUE self, VALUE env)
761 struct clogger *c = clogger_get(self);
763 if (c->wrap_body) {
764 VALUE tmp;
766 if (c->reentrant < 0) {
767 tmp = rb_hash_aref(env, g_rack_multithread);
768 c->reentrant = Qfalse == tmp ? 0 : 1;
770 if (c->reentrant) {
771 self = rb_obj_dup(self);
772 c = clogger_get(self);
775 ccall(c, env);
776 tmp = rb_ary_dup(c->response);
777 rb_ary_store(tmp, 2, self);
778 return tmp;
781 ccall(c, env);
782 cwrite(c);
784 return c->response;
787 /* :nodoc */
788 static VALUE clogger_init_copy(VALUE clone, VALUE orig)
790 struct clogger *a = clogger_get(orig);
791 struct clogger *b = clogger_get(clone);
793 memcpy(b, a, sizeof(struct clogger));
794 init_buffers(b);
796 return clone;
799 #define CONST_GLOBAL_STR2(var, val) do { \
800 g_##var = rb_obj_freeze(rb_str_new(val, sizeof(val) - 1)); \
801 rb_global_variable(&g_##var); \
802 } while (0)
804 #define CONST_GLOBAL_STR(val) CONST_GLOBAL_STR2(val, #val)
806 static void init_bad_response(void)
808 g_bad_app_response = rb_ary_new();
809 rb_ary_store(g_bad_app_response, 0, INT2NUM(500));
810 rb_ary_store(g_bad_app_response, 1, rb_obj_freeze(rb_hash_new()));
811 rb_ary_store(g_bad_app_response, 2, rb_obj_freeze(rb_ary_new()));
812 rb_obj_freeze(g_bad_app_response);
813 rb_global_variable(&g_bad_app_response);
816 void Init_clogger_ext(void)
818 ltlt_id = rb_intern("<<");
819 call_id = rb_intern("call");
820 each_id = rb_intern("each");
821 close_id = rb_intern("close");
822 to_i_id = rb_intern("to_i");
823 to_s_id = rb_intern("to_s");
824 size_id = rb_intern("size");
825 cClogger = rb_define_class("Clogger", rb_cObject);
826 mFormat = rb_define_module_under(cClogger, "Format");
827 rb_define_alloc_func(cClogger, clogger_alloc);
828 rb_define_method(cClogger, "initialize", clogger_init, -1);
829 rb_define_method(cClogger, "initialize_copy", clogger_init_copy, 1);
830 rb_define_method(cClogger, "call", clogger_call, 1);
831 rb_define_method(cClogger, "each", clogger_each, 0);
832 rb_define_method(cClogger, "close", clogger_close, 0);
833 rb_define_method(cClogger, "fileno", clogger_fileno, 0);
834 rb_define_method(cClogger, "wrap_body?", clogger_wrap_body, 0);
835 rb_define_method(cClogger, "reentrant?", clogger_reentrant, 0);
836 CONST_GLOBAL_STR(REMOTE_ADDR);
837 CONST_GLOBAL_STR(HTTP_X_FORWARDED_FOR);
838 CONST_GLOBAL_STR(REQUEST_METHOD);
839 CONST_GLOBAL_STR(PATH_INFO);
840 CONST_GLOBAL_STR(QUERY_STRING);
841 CONST_GLOBAL_STR(REQUEST_URI);
842 CONST_GLOBAL_STR(HTTP_VERSION);
843 CONST_GLOBAL_STR2(rack_errors, "rack.errors");
844 CONST_GLOBAL_STR2(rack_input, "rack.input");
845 CONST_GLOBAL_STR2(rack_multithread, "rack.multithread");
846 CONST_GLOBAL_STR2(dash, "-");
847 CONST_GLOBAL_STR2(empty, "");
848 CONST_GLOBAL_STR2(space, " ");
849 CONST_GLOBAL_STR2(question_mark, "?");
850 CONST_GLOBAL_STR2(rack_request_cookie_hash, "rack.request.cookie_hash");
851 init_bad_response();