Hi Quixote users -- I'm just guessing the Quixote list has the
most number of SCGI users, so I thought I'd send this here.

I've had a few problems with SCGI 1.2.  The attached patch:

* Fixes the connection retrying -- when addr wasn't reconstructed with
each retry, the second retry would always fail with Invalid Argument
error, not ECONNREFUSED.  Probably increasing the retry delay *2 each
time is also too aggressive -- I like the idea of increasing the delay,
but probably not past 5 seconds.

* Sends all environmental variables, not just a fixed set.

* Writes those variables directly to the request stream, instead of
creating an intermediate structure and without mallocing anything.

I don't do this C thing that often, so if you can look at send_headers()
for any problems or possible overflows, that would be appreciated.

Some (all?) of this stuff probably belongs in the Apache code as well,
but I haven't had a chance to look at it.  Sending all the environmental
variables is the biggest reason I'd want to use SCGI over an HTTP proxy
-- you get access to things like REMOTE_USER.

Another thing that would be nice for mod_scgi would be a different way
of configuring, more like FastCGI, that might look like:

   SCGIMount /url 127.0.0.1:3000

This would attach it to that URL, and set SCRIPT_NAME and PATH_INFO
appropriately (SCRIPT_NAME to "/url").  Right now PATH_INFO doesn't get
set at all for mod_scgi requests (since they are based on location
matching typically, not attached to a particular URL), which means you
have to configure SCRIPT_NAME elsewhere, and that's fragile and confusing.

I'm don't really know how to make that change, since I've never really
looked into Apache modules, and that seems like a considerably different
kind of hook than what mod_scgi currently does.  It's somewhat similar
to FastCGI's FastCgiExternalServer command.

Communication over named sockets would also be nice.  I haven't really
used those before; maybe that's only a small change.  With named sockets
you don't need to manage a registry of all the servers and ports on a
box, which would be helpful.

Altogether I think this would fill in most of the gaps I see compared to
FastCGI (or at least the gaps I care about).

--
Ian Bicking  /  [EMAIL PROTECTED]  /  http://blog.ianbicking.org
--- orig/scgi-1.2/cgi2scgi.c	Thu Jun 17 14:59:28 2004
+++ cgi2scgi.c	Fri Jun 10 13:04:16 2005
@@ -25,12 +25,7 @@
 
 #define SCGI_PROTOCOL_VERSION "1"
 
-
-struct scgi_header {
-	struct scgi_header *next;
-	char *name;
-	char *value;
-};
+extern char **environ;
 
 static void
 die(void)
@@ -67,6 +62,7 @@
 		die_perror("parsing host IP");
 	}
 
+retry:
 	addr.sin_addr = host;
 	addr.sin_port = htons(PORT);
 	addr.sin_family = AF_INET;
@@ -77,7 +73,6 @@
 	}
 
 	/* connect */
-retry:
 	if (connect(sock, (struct sockaddr *)&addr, sizeof addr) == -1) {
 		if (errno == ECONNREFUSED && tries > 0) {
 			sleep(retrytime);
@@ -97,93 +92,44 @@
 	return sock;
 }
 
-static void
-add_header(struct scgi_header *list_head, char *name, char *value) 
-{
-	if (name != NULL && value != NULL) {
-		struct scgi_header *head = malloc(sizeof(struct scgi_header));
-		if (!head)
-			die_msg("out of memory");
-		head->next = list_head->next;
-		list_head->next = head;
-		head->name = name;
-		head->value = value;
-		/*fprintf(stderr, "%s = %s\n", name, value);*/
-	}
-}
-
-static void
-add_env_var(struct scgi_header *list_head, char *name)
-{
-	add_header(list_head, name, getenv(name));
-}
-
 static int
 send_headers(FILE *fp)
 {
-	char *v;
-	int n;
-	struct scgi_header *node;
-	struct scgi_header *t = malloc(sizeof(struct scgi_header));
-	if (!t)
-		die_msg("out of memory");
-	t->next = NULL;
-	t->name = NULL;
-	t->value = NULL;
-
-	/* add headers in reverse order */
-	add_env_var(t, "SERVER_SOFTWARE");
-	add_env_var(t, "SERVER_PROTOCOL");
-	add_env_var(t, "SERVER_NAME");
-	add_env_var(t, "SERVER_ADMIN");
-	add_env_var(t, "SERVER_ADDR");
-	add_env_var(t, "SERVER_PORT");
-	add_env_var(t, "REMOTE_ADDR");
-	add_env_var(t, "REMOTE_PORT");
-	add_env_var(t, "REMOTE_USER");
-	add_env_var(t, "REQUEST_METHOD");
-	add_env_var(t, "REQUEST_URI");
-	add_env_var(t, "QUERY_STRING");
-	add_env_var(t, "SCRIPT_NAME");
-	add_env_var(t, "PATH_INFO");
-	add_env_var(t, "HTTPS");
-	add_env_var(t, "CONTENT_TYPE");
-	add_env_var(t, "DOCUMENT_ROOT");
-	add_env_var(t, "HTTP_ACCEPT");
-	add_env_var(t, "HTTP_ACCEPT_CHARSET");
-	add_env_var(t, "HTTP_ACCEPT_ENCODING");
-	add_env_var(t, "HTTP_ACCEPT_LANGUAGE");
-	add_env_var(t, "HTTP_AUTHORIZATION");
-	add_env_var(t, "HTTP_COOKIE");
-	add_env_var(t, "HTTP_EXPECT");
-	add_env_var(t, "HTTP_FROM");
-	add_env_var(t, "HTTP_HOST");
-	add_env_var(t, "HTTP_IF_MATCH");
-	add_env_var(t, "HTTP_IF_MODIFIED_SINCE");
-	add_env_var(t, "HTTP_IF_NONE_MATCH");
-	add_env_var(t, "HTTP_IF_RANGE");
-	add_env_var(t, "HTTP_IF_UNMODIFIED_SINCE");
-	add_env_var(t, "HTTP_REFERER");
-	add_env_var(t, "HTTP_TE");
-	add_env_var(t, "HTTP_USER_AGENT");
-	add_header(t, "SCGI", SCGI_PROTOCOL_VERSION);
-	/* CONTENT_LENGTH must come first and always be present */
-	v = getenv("CONTENT_LENGTH");
-	add_header(t, "CONTENT_LENGTH", v ? v : "0");
-
-	/* calculate length of header data (including nulls) */
-	n = 0;
-	for (node = t->next; node != NULL; node = node->next) {
-		n += strlen(node->name) + 1;
-		n += strlen(node->value) + 1;
+	int length;
+	char *eq_index;
+	char **envpointer;
+
+	/* Set the CONTENT_LENGTH header if it is not already set */
+	setenv("CONTENT_LENGTH", "0", 0);
+	setenv("SCGI", SCGI_PROTOCOL_VERSION, 1);
+
+	/* Calculate the total length of the headers */
+	length = 0;
+	for (envpointer = environ; *envpointer != NULL; *envpointer++) {
+		eq_index = index(*envpointer, '=');
+		if (eq_index == NULL)
+			continue;
+		length += strlen(*envpointer) + 1;
 	}
 
-	/* send header data as netstring */
-	if (fprintf(fp, "%u:", n) < 0) return 0;
-	for (node = t->next; node != NULL; node = node->next) {
-		if (fputs(node->name, fp) < 0) return 0;
+	if (fprintf(fp, "%u:", length) < 0) return 0;
+	/* Always write CONTENT_LENGTH first */
+	if (fputs("CONTENT_LENGTH", fp) < 0) return 0;
+	if (fputc('\0', fp) == EOF) return 0;
+	if (fputs(getenv("CONTENT_LENGTH"), fp) < 0) return 0;
+	if (fputc('\0', fp) == EOF) return 0;
+	for (envpointer = environ; *envpointer != NULL; *envpointer++) {
+		/*fprintf(stderr, "%s\n", *envpointer);*/
+		eq_index = index(*envpointer, '=');
+		if (eq_index == NULL)
+			continue;
+		if (! strncmp(*envpointer, "CONTENT_LENGTH", 14))
+			continue;
+		length = eq_index - (*envpointer);
+		if (fwrite(*envpointer, sizeof(char), length, fp) < length)
+			return 0;
 		if (fputc('\0', fp) == EOF) return 0;
-		if (fputs(node->value, fp) < 0) return 0;
+		if (fputs(eq_index+1, fp) < 0) return 0;
 		if (fputc('\0', fp) == EOF) return 0;
 	}
 	if (fputc(',', fp) == EOF) return 0;

/*
 * cgi2scgi: A CGI to SCGI translator
 *
 */

/* configuration settings */
#ifndef HOST
#define HOST "127.0.0.1"
#endif

#ifndef PORT
#define PORT 3000
#endif


#include <stdlib.h>
#include <stdio.h>
#include <string.h>
#include <errno.h>
#include <unistd.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <arpa/inet.h>
#include <netinet/tcp.h> /* for TCP_NODELAY */

#define SCGI_PROTOCOL_VERSION "1"

extern char **environ;

static void
die(void)
{
	_exit(2);
}

static void
die_perror(char *msg)
{
	char buf[500];
	snprintf(buf, sizeof buf, "error: %s", msg);
	perror(buf);
	die();
}

static void
die_msg(char *msg)
{
	fprintf(stderr, "error: %s\n", msg);
	die();
}

static int
open_socket(void)
{
	int sock, set;
	int tries = 5, retrytime = 1;
	struct in_addr host;
	struct sockaddr_in addr;

	/* create socket */
	if (!inet_aton(HOST, &(host))) {
		die_perror("parsing host IP");
	}

retry:
	addr.sin_addr = host;
	addr.sin_port = htons(PORT);
	addr.sin_family = AF_INET;

	sock = socket(PF_INET, SOCK_STREAM, 0);
	if (sock == -1) {
		die_perror("creating socket");
	}

	/* connect */
	if (connect(sock, (struct sockaddr *)&addr, sizeof addr) == -1) {
		if (errno == ECONNREFUSED && tries > 0) {
			sleep(retrytime);
			tries--;
			retrytime *= 2;
			goto retry;
		}
		die_perror("connecting to server");
	}

#ifdef TCP_NODELAY
	/* disable Nagle */
	set = 1;
	setsockopt(sock, IPPROTO_TCP, TCP_NODELAY, (char *)&set, sizeof(set));
#endif

	return sock;
}

static int
send_headers(FILE *fp)
{
	int length;
	char *eq_index;
	char **envpointer;

	/* Set the CONTENT_LENGTH header if it is not already set */
	setenv("CONTENT_LENGTH", "0", 0);
	setenv("SCGI", SCGI_PROTOCOL_VERSION, 1);

	/* Calculate the total length of the headers */
	length = 0;
	for (envpointer = environ; *envpointer != NULL; *envpointer++) {
		eq_index = index(*envpointer, '=');
		if (eq_index == NULL)
			continue;
		length += strlen(*envpointer) + 1;
	}

	if (fprintf(fp, "%u:", length) < 0) return 0;
	/* Always write CONTENT_LENGTH first */
	if (fputs("CONTENT_LENGTH", fp) < 0) return 0;
	if (fputc('\0', fp) == EOF) return 0;
	if (fputs(getenv("CONTENT_LENGTH"), fp) < 0) return 0;
	if (fputc('\0', fp) == EOF) return 0;
	for (envpointer = environ; *envpointer != NULL; *envpointer++) {
		/*fprintf(stderr, "%s\n", *envpointer);*/
		eq_index = index(*envpointer, '=');
		if (eq_index == NULL)
			continue;
		if (! strncmp(*envpointer, "CONTENT_LENGTH", 14))
			continue;
		length = eq_index - (*envpointer);
		if (fwrite(*envpointer, sizeof(char), length, fp) < length)
			return 0;
		if (fputc('\0', fp) == EOF) return 0;
		if (fputs(eq_index+1, fp) < 0) return 0;
		if (fputc('\0', fp) == EOF) return 0;
	}
	if (fputc(',', fp) == EOF) return 0;
	return 1;
}

static int
copyfp(FILE *in, FILE *out)
{
	size_t n, n2;
	char buf[8000];
	for (;;) {
		n = fread(buf, 1, sizeof buf, in);
		if (n != sizeof buf && ferror(in))
			return 0;
		if (n == 0)
			break; /* EOF */
		n2 = fwrite(buf, 1, n, out);
		if (n2 != n)
			return 0;
	}
	return 1;
}

int main(int argc, char **argv)
{
	int sock, fd;
	FILE *fp;

	sock = open_socket();

	/* send request */
	if ((fd = dup(sock)) < 0)
		die_perror("duplicating fd");
	if ((fp = fdopen(fd, "w")) == NULL)
		die_perror("creating buffered file");
	if (!send_headers(fp)) {
		die_msg("sending request headers");
	}
	if (!copyfp(stdin, fp)) {
		die_msg("sending request body");
	}
	if (fclose(fp) != 0)
		die_perror("sending request body");

	/* send reponse */
	if ((fd = dup(sock)) < 0 || (fp = fdopen(fd, "r")) == NULL)
		die_perror("creating buffered file from socket");
	if (!copyfp(fp, stdout)) {
		die_msg("sending response");
	}
	if (fclose(fp) != 0)
	    die_perror("closing socket");

	return 0;
}

_______________________________________________
Quixote-users mailing list
[email protected]
http://mail.mems-exchange.org/mailman/listinfo/quixote-users

Reply via email to