Hi,

I have implemented an SCGI patch for mathopd-1.6b9.

You use it with:
Specials { SCGI { .scgi }}
where the .scgi file is a unix domain socket to the SCGI application.
Also, the SCGI application should have a large enough listen queue to
accomodate the number of simultaneous connections configured in mathopd,
or there is a possibility to get 503 code errors under load.

The idea is this:
* process_cgi was renamed into process_cgi_scgi. It builds the CGI
  environment as before, then calls either process_cgi or process_scgi.
* process_cgi forkexecs the CGI script. The master process keeps
  a socketpair to talk with it.
* process_scgi connects to an existing SCGI unix socket socket, and the
  master process keeps that socket to talk with it. The client_input pool
  is prefilled with the CGI environment variables converted to SCGI format.
* calling writetochild directly at the end of process_scgi is not necessary
  for correctness, but I found out that it made a large difference (35%) in
  performance when the SCGI serves small (3KB) cached replies.
* I modified get_path_info to consider cases where the open call returns
  ENXIO (as happens with unix sockets). When this happens, the finfo field
  of the request is not filled in. A new field 'valid_finfo' was added to
  track the validity of the finfo field.

P.S. the patch also backports easily to 1.5 based versions.

Hope this helps,

-- 
Michel "Walken" Lespinasse
A program is never fully debugged until the last user dies.
diff -ru mathopd-1.6b9/src/cgi.c mathopd-1.6b9-scgi/src/cgi.c
--- mathopd-1.6b9/src/cgi.c	2004-04-25 12:08:57.000000000 -0700
+++ mathopd-1.6b9-scgi/src/cgi.c	2008-09-08 01:15:54.000000000 -0700
@@ -45,6 +45,7 @@
 #include <stdio.h>
 #include <unistd.h>
 #include <ctype.h>
+#include <sys/un.h>
 #include "mathopd.h"
 
 struct cgi_parameters {
@@ -195,19 +196,24 @@
 	return 0;
 }
 
-static int make_cgi_envp(struct request *r, struct cgi_parameters *cp)
+static int make_cgi_envp(struct request *r, struct cgi_parameters *cp, int scgi)
 {
 	char t[16];
 	struct simple_list *e;
 	char path_translated[PATHLEN];
 	char *tmp;
 
+	tmp = r->in_content_length;
+	if (scgi && !tmp)
+		tmp = "0";	// SCGI scripts always want a CONTENT_LENGTH.
+	if (add("CONTENT_LENGTH", tmp, 0, cp) == -1)
+		return -1;
+	if (scgi && add("SCGI", "1", 0, cp) == -1)
+		return -1;
 	if (add_http_vars(r, cp) == -1)
 		return -1;
 	if (add("GATEWAY_INTERFACE", "CGI/1.1", 0, cp) == -1)
 		return -1;
-	if (add("CONTENT_LENGTH", r->in_content_length, 0, cp) == -1)
-		return -1;
 	if (add("CONTENT_TYPE", r->in_content_type, 0, cp) == -1)
 		return -1;
 	if (r->user && r->user[0]) {
@@ -305,9 +311,9 @@
 	return 0;
 }
 
-static int init_cgi_env(struct request *r, struct cgi_parameters *cp)
+static int init_cgi_env(struct request *r, struct cgi_parameters *cp, int scgi)
 {
-	if (make_cgi_envp(r, cp) == -1)
+	if (make_cgi_envp(r, cp, scgi) == -1)
 		return -1;
 	if (make_cgi_argv(r, cp) == -1)
 		return -1;
@@ -332,15 +338,40 @@
 	}
 }
 
-int process_cgi(struct request *r)
+static int process_scgi(struct request *, struct cgi_parameters *);
+static int process_cgi(struct request *, struct cgi_parameters *);
+
+int process_cgi_scgi(struct request *r, int scgi)
 {
 	struct cgi_parameters c;
+	int ok;
+
+	c.cgi_envc = 0;
+	c.cgi_envp = 0;
+	c.cgi_argc = 0;
+	c.cgi_argv = 0;
+	if (init_cgi_env(r, &c, scgi) == -1) {
+		log_d("process_cgi_scgi: out of memory");
+		destroy_parameters(&c);
+		r->status = 500;
+		return 0;
+	}
+	if (scgi)
+		ok = process_scgi (r, &c);
+	else
+		ok = process_cgi (r, &c);
+	destroy_parameters(&c);
+	return ok;
+}
+
+int process_cgi(struct request *r, struct cgi_parameters *c)
+{
 	uid_t u;
 	gid_t g;
 	int p[2], efd;
 	pid_t pid;
 
-	if (r->curdir[0] == 0) {
+	if (r->curdir[0] == 0 || !r->valid_finfo) {
 		r->status = 500;
 		return 0;
 	}
@@ -374,19 +405,8 @@
 		r->status = 500;
 		return 0;
 	}
-	c.cgi_envc = 0;
-	c.cgi_envp = 0;
-	c.cgi_argc = 0;
-	c.cgi_argv = 0;
-	if (init_cgi_env(r, &c) == -1) {
-		log_d("process_cgi: out of memory");
-		destroy_parameters(&c);
-		r->status = 500;
-		return 0;
-	}
 	if (socketpair(AF_UNIX, SOCK_STREAM, 0, p) == -1) {
 		lerror("socketpair");
-		destroy_parameters(&c);
 		r->status = 500;
 		return 0;
 	}
@@ -397,29 +417,74 @@
 		if (efd == -1) {
 			close(p[0]);
 			close(p[1]);
-			destroy_parameters(&c);
 			r->status = 500;
 			return 0;
 		}
 		fcntl(efd, F_SETFD, FD_CLOEXEC);
 	} else
 		efd = -1;
-	pid = spawn(c.cgi_argv[0], c.cgi_argv, c.cgi_envp, p[1], efd, u, g, r->curdir);
+	pid = spawn(c->cgi_argv[0], c->cgi_argv, c->cgi_envp, p[1], efd, u, g, r->curdir);
 	if (efd != -1)
 		close(efd);
 	close(p[1]);
 	if (pid == -1) {
 		close(p[0]);
-		destroy_parameters(&c);
 		r->cn->keepalive = 0;
 		r->status = 503;
 		return 0;
 	}
 	fcntl(p[0], F_SETFL, O_NONBLOCK);
 	init_child(r->cn, p[0]);
-	destroy_parameters(&c);
 	if (debug)
 		log_d("process_cgi: %d", p[0]);
 	r->forked = 1;
 	return -1;
 }
+
+int process_scgi(struct request *r, struct cgi_parameters *c)
+{
+	int sock;
+	struct sockaddr_un addr;
+	char **p;
+	int n;
+	struct pool *scgi;
+
+	sock = socket(PF_UNIX, SOCK_STREAM, 0);
+	fcntl(sock, F_SETFL, O_NONBLOCK);
+	addr.sun_family = AF_UNIX;
+	strncpy(addr.sun_path, r->path_translated, sizeof(addr.sun_path));
+	addr.sun_path[sizeof(addr.sun_path)-1] = 0;
+	if (connect(sock, (struct sockaddr *)&addr, sizeof addr) == -1) {
+		log_d("scgi: could not connect to %s", addr.sun_path);
+		lerror("connect");
+		r->status = 503;
+		return 0;
+	}
+
+	n = 0;
+	for (p = c->cgi_envp; *p != NULL; p++)
+		if (strchr(*p, '='))
+			n += strlen (*p) + 1;
+
+	scgi = &r->cn->client_input;
+	if (scgi->floor + n + 16 > scgi->ceiling) {
+		log_d("scgi: ScriptBufSize is too small");
+		r->status = 500;
+		return 0;
+	}
+
+	init_child(r->cn, sock);
+	pool_print (scgi, "%u:", n);
+	for (p = c->cgi_envp; *p != NULL; p++) {
+		char * eq = strchr(*p, '=');
+		if (eq) {
+			*eq = 0;
+			pool_print (scgi, "%s%c%s%c", *p, 0, eq+1, 0);
+		}
+	}
+	pool_print (scgi, ",");
+	writetochild (r->cn);
+
+	r->forked = 1;
+	return -1;
+}
diff -ru mathopd-1.6b9/src/mathopd.h mathopd-1.6b9-scgi/src/mathopd.h
--- mathopd-1.6b9/src/mathopd.h	2007-07-07 09:04:12.000000000 -0700
+++ mathopd-1.6b9-scgi/src/mathopd.h	2008-09-08 00:00:19.000000000 -0700
@@ -50,6 +50,7 @@
 #endif
 
 #define CGI_MAGIC_TYPE "CGI"
+#define SCGI_MAGIC_TYPE "SCGI"
 #define IMAP_MAGIC_TYPE "Imagemap"
 #define REDIRECT_MAGIC_TYPE "Redirect"
 #define DUMP_MAGIC_TYPE "Dump"
@@ -251,6 +252,7 @@
 	int method;
 	int status;
 	struct control *c;
+	int valid_finfo;
 	struct stat finfo;
 	int isindex;
 	const char *error_file;
@@ -421,7 +423,7 @@
 
 /* cgi */
 
-extern int process_cgi(struct request *);
+extern int process_cgi_scgi(struct request *, int);
 
 /* dump */
 
@@ -457,6 +459,7 @@
 extern int init_cgi_headers(void);
 extern void pipe_run(struct connection *);
 extern void init_child(struct connection *, int);
+extern int writetochild(struct connection *);
 extern int setup_child_pollfds(int, struct connection *);
 
 #if defined LINUX_SENDFILE || defined FREEBSD_SENDFILE
diff -ru mathopd-1.6b9/src/request.c mathopd-1.6b9-scgi/src/request.c
--- mathopd-1.6b9/src/request.c	2007-07-21 03:41:13.000000000 -0700
+++ mathopd-1.6b9-scgi/src/request.c	2008-09-08 00:11:04.000000000 -0700
@@ -323,6 +323,7 @@
 		lerror("fstat");
 		return -1;
 	}
+	r->valid_finfo = 1;
 	close_rfd(r);
 	r->cn->rfd = fd;
 	return 0;
@@ -331,7 +332,7 @@
 static int get_path_info(struct request *r)
 {
 	char *p, *pa, *end, *cp, *start, *cds;
-	int fd, first;
+	int fd, first, found;
 	size_t m;
 
 	m = r->location_length;
@@ -343,7 +344,7 @@
 	pa = r->path_args;
 	*pa = 0;
 	cp = end;
-	first = 0;
+	first = found = 0;
 	while (cp >= start && cp[-1] == '/')
 		--cp;
 	while (cp >= start) {
@@ -357,12 +358,16 @@
 			case ENOENT:
 			case ENOTDIR:
 				break;
+			case ENXIO:
+				found = 1;
+				break;
 			default:
 				log_d("cannot open %s", p);
 				lerror("open");
 				return -1;
 			}
 		else {
+			found = 1;
 			if (assign_rfd(r, fd) == -1) {
 				close(fd);
 				return -1;
@@ -374,9 +379,9 @@
 		}
 		if (cp != end)
 			*cp = '/';
-		if (fd != -1) {
+		if (found) {
 			strcpy(pa, cp);
-			if (S_ISDIR(r->finfo.st_mode))
+			if (r->valid_finfo && S_ISDIR(r->finfo.st_mode))
 				*cp++ = '/';
 			else if (first) {
 				cds = strrchr(r->curdir, '/');
@@ -591,7 +596,7 @@
 static int process_external(struct request *r)
 {
 	r->num_content = -1;
-	return process_cgi(r);
+	return process_cgi_scgi(r, 0);
 }
 
 static int process_special(struct request *r)
@@ -601,7 +606,9 @@
 	ct = r->content_type;
 	r->num_content = -1;
 	if (!strcasecmp(ct, CGI_MAGIC_TYPE))
-		return process_cgi(r);
+		return process_cgi_scgi(r, 0);
+	if (!strcasecmp(ct, SCGI_MAGIC_TYPE))
+		return process_cgi_scgi(r, 1);
 	if (r->status)
 		return 0;
 	if (r->method == M_POST) {
@@ -661,6 +668,13 @@
 
 static int process_fd(struct request *r)
 {
+	if (!r->valid_finfo || !S_ISREG(r->finfo.st_mode)) {
+		close_rfd(r);
+		log_d("%s is not a regular file", r->path_translated);
+		r->error_file = r->c->error_404_file;
+		r->status = 404;
+		return 1;
+	}
 	if (r->path_args[0] && r->c->path_args_ok == 0 && (r->path_args[1] || r->isindex == 0)) {
 		close_rfd(r);
 		if (debug)
@@ -919,7 +933,7 @@
 		r->status = 404;
 		return 1;
 	}
-	if (S_ISDIR(r->finfo.st_mode)) {
+	if (r->valid_finfo && S_ISDIR(r->finfo.st_mode)) {
 		close_rfd(r);
 		if (r->status)
 			return 0;
@@ -953,13 +967,6 @@
 		r->status = 404;
 		return 1;
 	}
-	if (!S_ISREG(r->finfo.st_mode)) {
-		close_rfd(r);
-		log_d("%s is not a regular file", r->path_translated);
-		r->error_file = r->c->error_404_file;
-		r->status = 404;
-		return 1;
-	}
 	if (get_mime(r, r->path_translated) == -1) {
 		close_rfd(r);
 		log_d("get_mime failed for %s", r->path_translated);
@@ -1612,6 +1619,7 @@
 	r->status = 0;
 	r->isindex = 0;
 	r->c = 0;
+	r->valid_finfo = 0;
 	r->error_file = 0;
 	r->user[0] = 0;
 	r->location_length = 0;
diff -ru mathopd-1.6b9/src/stub.c mathopd-1.6b9-scgi/src/stub.c
--- mathopd-1.6b9/src/stub.c	2007-07-07 09:04:12.000000000 -0700
+++ mathopd-1.6b9-scgi/src/stub.c	2008-09-08 00:11:56.000000000 -0700
@@ -404,7 +404,7 @@
 	return 0;
 }
 
-static int writetochild(struct connection *p)
+int writetochild(struct connection *p)
 {
 	size_t bytestowrite;
 	ssize_t r;

Reply via email to