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;