Class: Agoo::Server

Inherits:
Object
  • Object
show all
Defined in:
ext/agoo/server.c,
ext/agoo/server.c

Overview

An HTTP server that support the rack API as well as some other optimized APIs for handling HTTP requests.

Class Method Summary collapse

Instance Method Summary collapse

Class Method Details

.new(*args) ⇒ Object

call-seq: new(port, root, options)

Creates a new server that will listen on the designated port and using the root as the root of the static resources. Logging is feature based and not level based and the options reflect that approach.

  • options [Hash] server options

    • :pedantic [true|false] if true response header and status codes are check and an exception raised if they violate the rack spec at github.com/rack/rack/blob/master/SPEC, tools.ietf.org/html/rfc3875#section-4.1.18, or tools.ietf.org/html/rfc7230.

    • :thread_count [Integer] number of ruby worker threads. Defaults to one. If zero then the start function will not return but instead will proess using the thread that called start. Usually the default is best unless the workers are making IO calls.

    • :log_dir [String] directory to place log files in. If nil or empty then no log files are written.

    • :log_console [true|false] if true log entry are display on the console.

    • :log_classic [true|false] if true log entry follow a classic format. If false log entries are JSON.

    • :log_colorize [true|false] if true log entries are colorized.

    • :log_states [Hash] a map of logging categories and whether they should be on or off. Categories are:

      • :ERROR errors

      • :WARN warnings

      • :INFO infomational

      • :DEBUG debugging

      • :connect openning and closing of connections

      • :request requests

      • :response responses

      • :eval handler evaluationss



184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
# File 'ext/agoo/server.c', line 184

static VALUE
server_new(int argc, VALUE *argv, VALUE self) {
    Server	s;
    struct _Err	err = ERR_INIT;
    int		port;
    const char	*root;
    VALUE	options = Qnil;
    
    if (argc < 2 || 3 < argc) {
	rb_raise(rb_eArgError, "Wrong number of arguments to Agoo::Server.new.");
    }
    port = FIX2INT(argv[0]);
    rb_check_type(argv[1], T_STRING);
    root = StringValuePtr(argv[1]);
    if (3 <= argc) {
	options = argv[2];
    }
    s = ALLOC(struct _Server);
    memset(s, 0, sizeof(struct _Server));
    if (ERR_OK != configure(&err, s, port, root, options)) {
	xfree(s);
	// TBD raise Agoo specific exception
	rb_raise(rb_eArgError, "%s", err.msg);
    }
    queue_multi_init(&s->con_queue, 256, false, false);
    queue_multi_init(&s->eval_queue, 1024, false, true);

    cache_init(&s->pages);
    the_server = s;
    
    return Data_Wrap_Struct(server_class, NULL, server_free, s);
}

Instance Method Details

#add_mime(suffix, type) ⇒ Object

call-seq: add_mime(suffix, type)

Adds a mime type by associating a type string with a suffix. This is used for static files.



896
897
898
899
900
901
# File 'ext/agoo/server.c', line 896

static VALUE
add_mime(VALUE self, VALUE suffix, VALUE type) {
    mime_set(&((Server)DATA_PTR(self))->pages, StringValuePtr(suffix), StringValuePtr(type));

    return Qnil;
}

#debug(msg) ⇒ Object

call-seq: debug(msg)

Log a debug message.



743
744
745
746
747
# File 'ext/agoo/server.c', line 743

static VALUE
log_debug(VALUE self, VALUE msg) {
    log_cat(&((Server)DATA_PTR(self))->debug_cat, "%s", StringValuePtr(msg));
    return Qnil;
}

#debug?Boolean

call-seq: debug?()

Returns true is debug entries are being logged.

Returns:

  • (Boolean)


685
686
687
688
# File 'ext/agoo/server.c', line 685

static VALUE
log_debugp(VALUE self) {
    return ((Server)DATA_PTR(self))->debug_cat.on ? Qtrue : Qfalse;
}

#error(msg) ⇒ Object

call-seq: error(msg)

Log an error message.



707
708
709
710
711
# File 'ext/agoo/server.c', line 707

static VALUE
log_error(VALUE self, VALUE msg) {
    log_cat(&((Server)DATA_PTR(self))->error_cat, "%s", StringValuePtr(msg));
    return Qnil;
}

#error?Boolean

call-seq: error?()

Returns true is errors are being logged.

Returns:

  • (Boolean)


652
653
654
655
# File 'ext/agoo/server.c', line 652

static VALUE
log_errorp(VALUE self) {
    return ((Server)DATA_PTR(self))->error_cat.on ? Qtrue : Qfalse;
}

#handle(method, pattern, handler) ⇒ Object

call-seq: handle(method, pattern, handler)

Registers a handler for the HTTP method and path pattern specified. The path pattern follows glob like rules in that a single * matches a single token bounded by the ‘/` character and a double ** matches all remaining.



822
823
824
825
826
827
828
829
830
831
832
833
834
835
836
837
838
839
840
841
842
843
844
845
846
847
848
849
850
851
852
853
854
855
856
857
858
859
860
861
862
863
864
865
866
867
868
# File 'ext/agoo/server.c', line 822

static VALUE
handle(VALUE self, VALUE method, VALUE pattern, VALUE handler) {
    Server	server = (Server)DATA_PTR(self);
    Hook	hook;
    Method	meth = ALL;
    const char	*pat;

    rb_check_type(pattern, T_STRING);
    pat = StringValuePtr(pattern);

    if (connect_sym == method) {
	meth = CONNECT;
    } else if (delete_sym == method) {
	meth = DELETE;
    } else if (get_sym == method) {
	meth = GET;
    } else if (head_sym == method) {
	meth = HEAD;
    } else if (options_sym == method) {
	meth = OPTIONS;
    } else if (post_sym == method) {
	meth = POST;
    } else if (put_sym == method) {
	meth = PUT;
    } else if (Qnil == method) {
	meth = ALL;
    } else {
	rb_raise(rb_eArgError, "invalid method");
    }
    if (NULL == (hook = hook_create(meth, pat, handler))) {
	rb_raise(rb_eStandardError, "out of memory.");
    } else {
	Hook	h;
	Hook	prev = NULL;

	for (h = server->hooks; NULL != h; h = h->next) {
	    prev = h;
	}
	if (NULL != prev) {
	    prev->next = hook;
	} else {
	    server->hooks = hook;
	}
	rb_gc_register_address(&hook->handler);
    }
    return Qnil;
}

#handle_not_found(handler) ⇒ Object

call-seq: not_found_handle(handler)

Registers a handler to be called when no other hook is found and no static file is found.



877
878
879
880
881
882
883
884
885
886
887
# File 'ext/agoo/server.c', line 877

static VALUE
handle_not_found(VALUE self, VALUE handler) {
    Server	server = (Server)DATA_PTR(self);

    if (NULL == (server->hook404 = hook_create(GET, "/", handler))) {
	rb_raise(rb_eStandardError, "out of memory.");
    }
    rb_gc_register_address(&server->hook404->handler);
    
    return Qnil;
}

#info(msg) ⇒ Object

call-seq: info(msg)

Log an info message.



731
732
733
734
735
# File 'ext/agoo/server.c', line 731

static VALUE
log_info(VALUE self, VALUE msg) {
    log_cat(&((Server)DATA_PTR(self))->info_cat, "%s", StringValuePtr(msg));
    return Qnil;
}

#info?Boolean

call-seq: info?()

Returns true is info entries are being logged.

Returns:

  • (Boolean)


674
675
676
677
# File 'ext/agoo/server.c', line 674

static VALUE
log_infop(VALUE self) {
    return ((Server)DATA_PTR(self))->info_cat.on ? Qtrue : Qfalse;
}

#log_eval(msg) ⇒ Object

call-seq: log_eval(msg)

Log an eval message.



755
756
757
758
759
# File 'ext/agoo/server.c', line 755

static VALUE
log_eval(VALUE self, VALUE msg) {
    log_cat(&((Server)DATA_PTR(self))->eval_cat, "%s", StringValuePtr(msg));
    return Qnil;
}

#log_eval?Boolean

call-seq: eval?()

Returns true is handler evaluation entries are being logged.

Returns:

  • (Boolean)


696
697
698
699
# File 'ext/agoo/server.c', line 696

static VALUE
log_evalp(VALUE self) {
    return ((Server)DATA_PTR(self))->eval_cat.on ? Qtrue : Qfalse;
}

#log_flush(to) ⇒ Object

call-seq: log_flush()

Flush the log queue and write all entries to disk or the console. The call waits for the flush to complete.



804
805
806
807
808
809
810
811
812
# File 'ext/agoo/server.c', line 804

static VALUE
server_log_flush(VALUE self, VALUE to) {
    double	timeout = NUM2DBL(to);
    
    if (!log_flush(&((Server)DATA_PTR(self))->log, timeout)) {
	rb_raise(rb_eStandardError, "timed out waiting for log flush.");
    }
    return Qnil;
}

#log_state(label) ⇒ Object

call-seq: log_state(label)

Return the logging state of the category identified by the label.



767
768
769
770
771
772
773
774
775
776
# File 'ext/agoo/server.c', line 767

static VALUE
log_state(VALUE self, VALUE label) {
    Server	server = (Server)DATA_PTR(self);
    LogCat	cat = log_cat_find(&server->log, StringValuePtr(label));

    if (NULL == cat) {
	rb_raise(rb_eArgError, "%s is not a valid log category.", StringValuePtr(label));
    }
    return cat->on ? Qtrue : Qfalse;
}

#set_log_state(label, on) ⇒ Object

call-seq: set_log_state(label, state)

Set the logging state of the category identified by the label.



784
785
786
787
788
789
790
791
792
793
794
795
# File 'ext/agoo/server.c', line 784

static VALUE
set_log_state(VALUE self, VALUE label, VALUE on) {
    Server	server = (Server)DATA_PTR(self);
    LogCat	cat = log_cat_find(&server->log, StringValuePtr(label));

    if (NULL == cat) {
	rb_raise(rb_eArgError, "%s is not a valid log category.", StringValuePtr(label));
    }
    cat->on = (Qtrue == on);

    return Qnil;
}

#shutdownObject

call-seq: shutdown()

Shutdown the server. Logs and queues are flushed before shutting down.



640
641
642
643
644
# File 'ext/agoo/server.c', line 640

static VALUE
server_shutdown(VALUE self) {
    shutdown_server((Server)DATA_PTR(self));
    return Qnil;
}

#startObject

call-seq: start()

Start the server.



566
567
568
569
570
571
572
573
574
575
576
577
578
579
580
581
582
583
584
585
586
587
588
589
590
591
592
593
594
595
596
597
598
599
600
601
602
603
604
605
606
607
608
609
610
611
612
613
614
615
616
617
618
619
620
621
622
623
624
625
626
627
628
629
630
631
632
# File 'ext/agoo/server.c', line 566

static VALUE
start(VALUE self) {
    Server	server = (Server)DATA_PTR(self);
    VALUE	*vp;
    int		i;
    double	giveup;
    
    server->active = true;
    server->ready = false;

    pthread_create(&server->listen_thread, NULL, listen_loop, server);
    pthread_create(&server->con_thread, NULL, con_loop, server);
    
    giveup = dtime() + 1.0;
    while (dtime() < giveup) {
	if (server->ready) {
	    break;
	}
	dsleep(0.001);
    }
    signal(SIGINT, sig_handler);
    signal(SIGTERM, sig_handler);
    signal(SIGPIPE, SIG_IGN);

    if (server->info_cat.on) {
	VALUE	agoo = rb_const_get_at(rb_cObject, rb_intern("Agoo"));
	VALUE	v = rb_const_get_at(agoo, rb_intern("VERSION"));
					       
	log_cat(&server->info_cat, "Agoo %s listening on port %d.", StringValuePtr(v), server->port);
    }
    if (0 >= server->thread_cnt) {
	Req		req;

	while (server->active) {
	    if (NULL != (req = (Req)queue_pop(&server->eval_queue, 0.1))) {
		switch (req->handler_type) {
		case BASE_HOOK:
		    handle_base(req);
		    break;
		case RACK_HOOK:
		    handle_rack(req);
		    break;
		case WAB_HOOK:
		    handle_wab(req);
		    break;
		default: {
		    char	buf[256];
		    int		cnt = snprintf(buf, sizeof(buf), "HTTP/1.1 500 Internal Error\r\nConnection: Close\r\nContent-Length: 0\r\n\r\n");
		    Text	message = text_create(buf, cnt);
	
		    req->res->close = true;
		    res_set_message(req->res, message);
		    queue_wakeup(&req->server->con_queue);
		    break;
		}
		}
	    }
	}
    } else {
	server->eval_threads = ALLOC_N(VALUE, server->thread_cnt + 1);
	for (i = server->thread_cnt, vp = server->eval_threads; 0 < i; i--, vp++) {
	    *vp = rb_thread_create(wrap_process_loop, server);
	}
	*vp = Qnil;
    }
    return Qnil;
}

#warn(msg) ⇒ Object

call-seq: warn(msg)

Log a warn message.



719
720
721
722
723
# File 'ext/agoo/server.c', line 719

static VALUE
log_warn(VALUE self, VALUE msg) {
    log_cat(&((Server)DATA_PTR(self))->warn_cat, "%s", StringValuePtr(msg));
    return Qnil;
}

#warn?Boolean

call-seq: warn?()

Returns true is warnings are being logged.

Returns:

  • (Boolean)


663
664
665
666
# File 'ext/agoo/server.c', line 663

static VALUE
log_warnp(VALUE self) {
    return ((Server)DATA_PTR(self))->warn_cat.on ? Qtrue : Qfalse;
}