aboutsummaryrefslogtreecommitdiffhomepage
path: root/src/c/fastcgi.c
diff options
context:
space:
mode:
authorGravatar Adam Chlipala <adamc@hcoop.net>2009-06-27 14:44:00 -0400
committerGravatar Adam Chlipala <adamc@hcoop.net>2009-06-27 14:44:00 -0400
commit688973e51e94d2c24185f0899227dfe7dd3dd93b (patch)
tree29f02cb11e27dc4394b44909fbaf79bda111f322 /src/c/fastcgi.c
parent74103b6a0ede67d270bb6850ae12578df62032fd (diff)
Serving Hello via FastCGI
Diffstat (limited to 'src/c/fastcgi.c')
-rw-r--r--src/c/fastcgi.c284
1 files changed, 266 insertions, 18 deletions
diff --git a/src/c/fastcgi.c b/src/c/fastcgi.c
index ad78b24a..57742ab3 100644
--- a/src/c/fastcgi.c
+++ b/src/c/fastcgi.c
@@ -65,11 +65,9 @@ static void fastcgi_input_reset(FCGI_Input *i) {
static int fastcgi_send(FCGI_Output *o,
unsigned char type,
- unsigned short requestId,
unsigned short contentLength) {
o->r.type = type;
- o->r.requestIdB1 = requestId >> 8;
- o->r.requestIdB0 = requestId & 255;
+ o->r.requestIdB1 = o->r.requestIdB0 = 0;
o->r.contentLengthB1 = contentLength >> 8;
o->r.contentLengthB0 = contentLength & 255;
return uw_really_send(o->sock, &o->r, sizeof(o->r) - (65535 - contentLength));
@@ -83,12 +81,12 @@ static FCGI_Record *fastcgi_recv(FCGI_Input *i) {
if (i->available >= i->used + sizeof(FCGI_Record) - 65535
&& i->available >= i->used + sizeof(FCGI_Record) - 65535
- + ((LATEST(i)->contentLengthB1 << 8) & LATEST(i)->contentLengthB0)
+ + ((LATEST(i)->contentLengthB1 << 8) | LATEST(i)->contentLengthB0)
+ LATEST(i)->paddingLength) {
FCGI_Record *r = LATEST(i);
i->used += sizeof(FCGI_Record) - 65535
- + ((LATEST(i)->contentLengthB1 << 8) & LATEST(i)->contentLengthB0)
+ + ((LATEST(i)->contentLengthB1 << 8) | LATEST(i)->contentLengthB0)
+ LATEST(i)->paddingLength;
return r;
@@ -109,16 +107,30 @@ static FCGI_Record *fastcgi_recv(FCGI_Input *i) {
}
}
-static char *get_header(void *data, const char *h) {
- return NULL;
-}
-
static void on_success(uw_context ctx) { }
static void on_failure(uw_context ctx) {
uw_write_header(ctx, "Status: 500 Internal Server Error\r\n");
}
+static int write_stdout(void *data, char *buf, size_t len) {
+ FCGI_Output *o = (FCGI_Output *)data;
+ while (len > 0) {
+ size_t len2 = len;
+ if (len2 > 65535)
+ len2 = 65535;
+ memcpy(o->r.contentData, buf, len2);
+ if (fastcgi_send(o, FCGI_STDOUT, len2)) {
+ fprintf(stderr, "fastcgi_send() failed in write_stdout().\n");
+ return -1;
+ }
+ buf += len2;
+ len -= len2;
+ }
+
+ return 0;
+}
+
static void write_stderr(FCGI_Output *o, const char *fmt, ...) {
int len;
va_list ap;
@@ -126,9 +138,14 @@ static void write_stderr(FCGI_Output *o, const char *fmt, ...) {
len = vsnprintf(o->r.contentData, 65535, fmt, ap);
if (len < 0)
- fprintf(stderr, "vsnprintf() failed in log_error().\n");
- else if (fastcgi_send(o, FCGI_STDERR, FCGI_NULL_REQUEST_ID, len))
- fprintf(stderr, "fastcgi_send() failed in log_error().\n");
+ fprintf(stderr, "vsnprintf() failed in write_stderr().\n");
+ else if (fastcgi_send(o, FCGI_STDERR, len))
+ fprintf(stderr, "fastcgi_send() failed in write_stderr().\n");
+}
+
+static void close_stream(FCGI_Output *o, unsigned char type) {
+ if (fastcgi_send(o, type, 0))
+ fprintf(stderr, "fastcgi_send() failed in close_stream().\n");
}
static void log_error(void *data, const char *fmt, ...) {
@@ -140,7 +157,7 @@ static void log_error(void *data, const char *fmt, ...) {
int len = vsnprintf(o->r.contentData, 65535, fmt, ap);
if (len < 0)
fprintf(stderr, "vsnprintf() failed in log_error().\n");
- else if (fastcgi_send(o, FCGI_STDERR, FCGI_NULL_REQUEST_ID, len))
+ else if (fastcgi_send(o, FCGI_STDERR, len))
fprintf(stderr, "fastcgi_send() failed in log_error().\n");
} else
vfprintf(stderr, fmt, ap);
@@ -149,21 +166,125 @@ static void log_error(void *data, const char *fmt, ...) {
static void log_debug(void *data, const char *fmt, ...) {
}
+static int read_funny_len(char **buf, int *len) {
+ if (*len <= 0)
+ return -1;
+
+ if ((*buf)[0] >> 7 == 0) {
+ int r = (*buf)[0];
+ ++*buf;
+ --*len;
+ return r;
+ }
+ else if (*len < 4)
+ return -1;
+ else {
+ int r = (((*buf)[3] & 0x7f) << 24) + ((*buf)[2] << 16) + ((*buf)[1] << 8) + (*buf)[0];
+ *buf += 4;
+ *len -= 4;
+ return r;
+ }
+}
+
+typedef struct {
+ char *name, *value;
+ unsigned name_len, value_len;
+} nvp;
+
+static char *search_nvps(nvp *nvps, const char *h) {
+ for (; nvps->name; ++nvps)
+ if (!strcmp(h, nvps->name))
+ return nvps->value;
+
+ return NULL;
+}
+
+typedef struct {
+ nvp *nvps;
+ char *uppercased;
+ int n_nvps, uppercased_len;
+} headers;
+
+static char *get_header(void *data, const char *h) {
+ headers *hs = (headers *)data;
+ size_t len = strlen(h);
+ char *s, *r;
+ const char *saved_h = h;
+
+ if (len > hs->uppercased_len) {
+ hs->uppercased_len = len;
+ hs->uppercased = realloc(hs->uppercased, len + 6);
+ }
+
+ strcpy(hs->uppercased, "HTTP_");
+ for (s = hs->uppercased+5; *h; ++h)
+ *s++ = *h == '-' ? '_' : toupper(*h);
+ *s = 0;
+
+ if (!strcasecmp(saved_h, "Content-length")
+ || !strcasecmp(saved_h, "Content-type"))
+ return search_nvps(hs->nvps, hs->uppercased + 5);
+ else
+ return search_nvps(hs->nvps, hs->uppercased);
+}
+
+static int read_nvp(char *buf, int len, nvp *nv) {
+ int nameLength, valueLength;
+
+ if ((nameLength = read_funny_len(&buf, &len)) < 0)
+ return -1;
+ if ((valueLength = read_funny_len(&buf, &len)) < 0)
+ return -1;
+ if (len < nameLength + valueLength)
+ return -1;
+
+ if (nameLength+1 > nv->name_len) {
+ nv->name_len = nameLength+1;
+ nv->name = realloc(nv->name, nv->name_len);
+ }
+ if (valueLength+1 > nv->value_len) {
+ nv->value_len = valueLength+1;
+ nv->value = realloc(nv->value, nv->value_len);
+ }
+
+ memcpy(nv->name, buf, nameLength);
+ nv->name[nameLength] = 0;
+
+ memcpy(nv->value, buf + nameLength, valueLength);
+ nv->value[valueLength] = 0;
+
+ return 0;
+}
+
static void *worker(void *data) {
int me = *(int *)data;
FCGI_Input *in = fastcgi_input();
FCGI_Output *out = fastcgi_output();
uw_context ctx = uw_request_new_context(out, log_error, log_debug);
uw_request_context rc = uw_new_request_context();
+ headers hs;
+ size_t body_size = 0;
+ char *body = malloc(0);
+ size_t path_size = 0;
+ char *path_buf = malloc(0);
+
+ hs.uppercased = malloc(0);
+ hs.uppercased_len = 0;
+ hs.nvps = malloc(sizeof(nvp));
+ hs.n_nvps = 1;
while (1) {
FCGI_Record *r;
+ size_t used_nvps = 0;
+ int body_len, body_read;
+ char *s;
+ char *method, *path, *path_info, *query_string;
in->sock = out->sock = uw_dequeue();
if (!(r = fastcgi_recv(in))) {
fprintf(stderr, "Error receiving initial message\n");
- return NULL;
+ goto done;
}
if (r->type != FCGI_BEGIN_REQUEST) {
@@ -174,14 +295,140 @@ static void *worker(void *data) {
goto done;
}
- if (!(r = fastcgi_recv(in))) {
- fprintf(stderr, "Error receiving second message\n");
- return NULL;
+ while (1) {
+ char *buf;
+
+ if (!(r = fastcgi_recv(in))) {
+ write_stderr(out, "Error receiving environment variables\n");
+ goto done;
+ }
+
+ if (r->type != FCGI_PARAMS) {
+ write_stderr(out, "Expected FCGI_PARAMS but got %d\n", r->type);
+ goto done;
+ }
+
+ if (r->contentLengthB1 == 0 && r->contentLengthB0 == 0)
+ break;
+
+ if (used_nvps == hs.n_nvps-1) {
+ ++hs.n_nvps;
+ hs.nvps = realloc(hs.nvps, hs.n_nvps * sizeof(nvp));
+ hs.nvps[used_nvps].name = malloc(0);
+ hs.nvps[used_nvps].value = malloc(0);
+ hs.nvps[used_nvps].name_len = 0;
+ hs.nvps[used_nvps].value_len = 0;
+ }
+
+ if (read_nvp(r->contentData, (r->contentLengthB1 << 8) | r->contentLengthB0, &hs.nvps[used_nvps]) < 0) {
+ write_stderr(out, "Error reading FCGI_PARAMS name-value pair\n");
+ goto done;
+ }
+
+ ++used_nvps;
+ }
+
+ hs.nvps[used_nvps].name = NULL;
+
+ if (s = get_header(&hs, "Content-Length")) {
+ body_len = atoi(s);
+ if (body_len < 0) {
+ write_stderr(out, "Invalid Content-Length\n");
+ goto done;
+ }
+ } else
+ body_len = 0;
+
+ if (body_len > body_size) {
+ body_size = body_len;
+ body = realloc(body, body_size);
+ }
+
+ for (body_read = 0; body_read < body_len; ) {
+ char *buf;
+ int this_len;
+
+ if (!(r = fastcgi_recv(in))) {
+ write_stderr(out, "Error receiving STDIN\n");
+ goto done;
+ }
+
+ if (r->type != FCGI_STDIN) {
+ write_stderr(out, "Expected FCGI_STDIN but got %d\n", r->type);
+ goto done;
+ }
+
+ if (r->contentLengthB1 == 0 && r->contentLengthB0 == 0) {
+ write_stderr(out, "End of STDIN\n");
+ break;
+ }
+
+ this_len = (r->contentLengthB1 << 8) | r->contentLengthB0;
+
+ if (body_read + this_len > body_len) {
+ write_stderr(out, "Too much STDIN\n");
+ goto done;
+ }
+
+ memcpy(&body[body_read], r->contentData, this_len);
+ body_read += this_len;
+ }
+
+ if (!(method = search_nvps(hs.nvps, "REQUEST_METHOD"))) {
+ write_stderr(out, "REQUEST_METHOD not set\n");
+ goto done;
+ }
+
+ if (!(path = search_nvps(hs.nvps, "SCRIPT_NAME"))) {
+ write_stderr(out, "SCRIPT_NAME not set\n");
+ goto done;
+ }
+
+ if (path_info = search_nvps(hs.nvps, "PATH_INFO")) {
+ int len1 = strlen(path), len2 = strlen(path_info);
+ int len = len1 + len2 + 1;
+
+ if (len > path_size) {
+ path_size = len;
+ path_buf = realloc(path_buf, path_size);
+ }
+
+ sprintf(path_buf, "%s%s", path, path_info);
+ path = path_buf;
+ }
+
+ if (!(query_string = search_nvps(hs.nvps, "QUERY_STRING")))
+ query_string = "";
+
+ uw_set_headers(ctx, get_header, &hs);
+ {
+ request_result rr;
+ FCGI_EndRequestBody *erb = (FCGI_EndRequestBody *)out->r.contentData;
+
+ rr = uw_request(rc, ctx, method, path, query_string, body, body_read,
+ on_success, on_failure,
+ out, log_error, log_debug,
+ in->sock);
+
+ if (rr == KEEP_OPEN)
+ goto done2;
+
+ uw_output(ctx, write_stdout, out);
+ close_stream(out, FCGI_STDOUT);
+ close_stream(out, FCGI_STDERR);
+
+ if (rr == SERVED)
+ erb->appStatusB3 = erb->appStatusB2 = erb->appStatusB1 = erb->appStatusB0 = 0;
+ else
+ erb->appStatusB3 = erb->appStatusB2 = erb->appStatusB1 = erb->appStatusB0 = 0xFF;
+
+ erb->protocolStatus = FCGI_REQUEST_COMPLETE;
+ fastcgi_send(out, FCGI_END_REQUEST, sizeof(FCGI_EndRequestBody));
}
- write_stderr(out, "Next message code: %d\n", r->type);
done:
close(in->sock);
+ done2:
fastcgi_input_reset(in);
uw_reset(ctx);
}
@@ -244,6 +491,7 @@ int main(int argc, char *argv[]) {
}
}
+ uw_set_on_success("");
uw_request_init(NULL, log_error, log_debug);
names = calloc(nthreads, sizeof(int));