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