Patch for ruby-fcgi
Current Ruby FastCGI module doesn’t allow you to specify port number or unix domain socket file path of connection.
I have create a patch to enable to specify port number or socket file path. If you have applied the patch, the following code is available.
require 'rubygems'
require 'fcgi'
arg = "/tmp/mysocket.sock" # or arg = 8001 # port number
FCGI.each(arg) do |req|
req.out.print "Content-Type: text/html\r\n"
req.out.print "\r\n"
req.out.print "<html><body>Hello World!</body></html>\n"
req.finish
end
The following is a patch for ruby-fcgi/ext/fcgi/fcgi.c:
--- fcgi.c.orig 2006-06-25 13:06:42.000000000 +0900
+++ fcgi.c 2007-12-17 00:11:14.000000000 +0900
@@ -49,15 +49,50 @@
free(data);
}
-static VALUE fcgi_s_accept(VALUE self)
-{
+static VALUE _fcgi_s_accept(VALUE self, int socket_fd);
+
+/**
+ * accept request.
+ *
+ * usage: FCGI.accept(socket=nil)
+ * - socket: socket (Fixnum). if nil then 0 (stdin) is used.
+ *
+ * ex1.
+ * listen_socket = FCGI.open(8081) # port number or socket file path
+ * while (socket = FCGI.accept(listen_socket)) != nil
+ * ...
+ * end
+ *
+ * ex2.
+ * while (socket = FCGI.accept()) != nil # default socket is 0 (stdin)
+ * ...
+ * end
+ *
+ */
+static VALUE fcgi_s_accept(int argc, VALUE argv[], VALUE self) {
+ VALUE socket;
+ int socket_fd;
+ if (argc == 0 || socket == Qnil) {
+ socket_fd = 0; /* use stdin as default socket file descriptor */
+ } else {
+ rb_scan_args(argc, argv, "01", &socket);
+ Check_Type(socket, T_FIXNUM);
+ socket_fd = FIX2INT(socket);
+ }
+ return _fcgi_s_accept(self, socket_fd);
+}
+
+/*
+ * body of fcgi_s_accept() and fcgi_s_accept2()
+ */
+static VALUE _fcgi_s_accept(VALUE self, int socket_fd) {
int status;
FCGX_Request *req;
fd_set readfds;
-
+
req = ALLOC(FCGX_Request);
-
- status = FCGX_InitRequest(req,0,0);
+
+ status = FCGX_InitRequest(req, socket_fd, 0);
if (status != 0) {
rb_raise(eFCGIError, "FCGX_Init() failed");
return Qnil;
@@ -110,14 +145,122 @@
}
}
-static VALUE fcgi_s_each(VALUE self)
-{
- VALUE fcgi;
-
- while ((fcgi = fcgi_s_accept(self)) != Qnil) {
- rb_yield(fcgi);
- }
- return Qnil;
+
+/**
+ * open socket with port number or socket file path.
+ *
+ * usage: FCGI.open(port_or_path, backlog=0)
+ * - port_or_path: port number or socket file path. 'nil' means $stdin.
+ * - backlog: number of backlog to pass listen(2)
+ *
+ * ex.
+ * listen_socket = FCGI.open(8081) # port number
+ *
+ * ex2.
+ * listen_socket = FCGI.open('/tmp/test1.sock') # socket file path
+ *
+ */
+static VALUE fcgi_s_open(int argc, VALUE *argv, VALUE self) {
+ VALUE arg, backlog;
+ int socket_fd;
+ rb_scan_args(argc, argv, "11", &arg, &backlog);
+ if (arg == Qnil) {
+ socket_fd = 0; /* default value of FCGX_OpenSocket() */
+ }
+ else {
+ char buf[10];
+ char *name;
+ int bklog;
+ /* port number or path name */
+ if (FIXNUM_P(arg)) { /* port number */
+ int port = FIX2INT(arg);
+ if (port >= (1 << 16)) {
+ rb_raise(rb_eArgError, "port number is too large.");
+ return Qnil;
+ }
+ if (port <= 0) {
+ rb_raise(rb_eArgError, "invalid port number.");
+ return Qnil;
+ }
+ sprintf(buf, ":%d\0", port);
+ name = buf;
+ }
+ else { /* path of unix domain socket */
+ Check_Type(arg, T_STRING);
+ name = RSTRING(arg)->ptr;
+ }
+ /* backlog for listen(2) */
+ if (backlog == Qnil) {
+ bklog = 0;
+ }
+ else {
+ Check_Type(backlog, T_FIXNUM);
+ bklog = FIX2INT(backlog);
+ }
+ /* create socket */
+ socket_fd = FCGX_OpenSocket(name, bklog);
+ if (socket_fd < -1) {
+ rb_raise(eFCGIError, "can't open socket.");
+ return Qnil;
+ }
+ }
+ return INT2FIX(socket_fd);
+}
+
+/**
+ * close socket
+ *
+ * usage: FCGI.close(socket)
+ * - socket: socket (Fixnum)
+ */
+static VALUE fcgi_s_close(VALUE self, VALUE socket) {
+ int socket_fd;
+ Check_Type(socket, T_FIXNUM);
+ socket_fd = FIX2INT(socket);
+ close(socket_fd);
+ return Qnil;
+}
+
+/**
+ * each-method with port number or socket file path
+ *
+ * usage: FCGI.each(port_or_path=nil, backlog=0)
+ * - port_or_path: port number or socket file path
+ * - backlog: number of backlog to pass to listen(2)
+ *
+ * ex.
+ * FCGI.each_with(8081) do |request|
+ * request.out.print "Content-Type: text/plain\r\n"
+ * request.out.print "\r\n"
+ * request.out.print "Hello World!\n"
+ * request.finish
+ * end
+ *
+ */
+static VALUE fcgi_s_each(int argc, VALUE argv[], VALUE self)
+{
+ VALUE fcgi, socket;
+ int socket_fd;
+ if (argc == 0) {
+ socket_fd = 0; /* use stdin as default socket file descriptor */
+ }
+ else {
+ socket = fcgi_s_open(argc, argv, self);
+ socket_fd = FIX2INT(socket);
+ /* change mode of unix socket file to 0777 */
+ /*
+ if (rb_type(argv[0]) == T_STRING && RSTRING(argv[0])->ptr[0] != ':') {
+ chmod(RSTRING(argv[0])->ptr, 0777);
+ }
+ */
+ }
+ while ((fcgi = _fcgi_s_accept(self, socket_fd)) != Qnil) {
+ rb_yield(fcgi);
+ }
+ if (argc != 0) {
+ fcgi_s_close(self, socket);
+ }
+ return Qnil;
}
static VALUE fcgi_s_iscgi(VALUE self)
@@ -513,9 +656,11 @@
cFCGI = rb_define_class("FCGI", rb_cObject);
eFCGIError =rb_define_class_under(cFCGI, "Error", rb_eStandardError);
- rb_define_singleton_method(cFCGI, "accept", fcgi_s_accept, 0);
- rb_define_singleton_method(cFCGI, "each", fcgi_s_each, 0);
- rb_define_singleton_method(cFCGI, "each_request", fcgi_s_each, 0);
+ rb_define_singleton_method(cFCGI, "accept", fcgi_s_accept, -1);
+ rb_define_singleton_method(cFCGI, "open", fcgi_s_open, -1);
+ rb_define_singleton_method(cFCGI, "close", fcgi_s_close, 1);
+ rb_define_singleton_method(cFCGI, "each", fcgi_s_each, -1);
+ rb_define_singleton_method(cFCGI, "each_request", fcgi_s_each, -1);
rb_define_singleton_method(cFCGI, "is_cgi?", fcgi_s_iscgi, 0);
rb_define_method(cFCGI, "in", fcgi_in, 0);
rb_define_method(cFCGI, "out", fcgi_out, 0);