annotate src/c/fastcgi.c @ 859:60240acd15b9

Successfully starting FastCGI sessions with Apache
author Adam Chlipala <adamc@hcoop.net>
date Sat, 27 Jun 2009 12:38:23 -0400
parents
children a738002d5b4d
rev   line source
adamc@859 1 #define _GNU_SOURCE
adamc@859 2
adamc@859 3 #include <stdio.h>
adamc@859 4 #include <string.h>
adamc@859 5 #include <stdlib.h>
adamc@859 6 #include <sys/types.h>
adamc@859 7 #include <sys/socket.h>
adamc@859 8 #include <netdb.h>
adamc@859 9 #include <netinet/in.h>
adamc@859 10 #include <unistd.h>
adamc@859 11 #include <signal.h>
adamc@859 12 #include <stdarg.h>
adamc@859 13
adamc@859 14 #include <pthread.h>
adamc@859 15
adamc@859 16 #include "urweb.h"
adamc@859 17 #include "request.h"
adamc@859 18 #include "queue.h"
adamc@859 19
adamc@859 20 #include "fastcgi.h"
adamc@859 21
adamc@859 22 typedef struct {
adamc@859 23 unsigned char version;
adamc@859 24 unsigned char type;
adamc@859 25 unsigned char requestIdB1;
adamc@859 26 unsigned char requestIdB0;
adamc@859 27 unsigned char contentLengthB1;
adamc@859 28 unsigned char contentLengthB0;
adamc@859 29 unsigned char paddingLength;
adamc@859 30 unsigned char reserved;
adamc@859 31 unsigned char contentData[65535];
adamc@859 32 } FCGI_Record;
adamc@859 33
adamc@859 34 typedef struct {
adamc@859 35 FCGI_Record r;
adamc@859 36 int sock;
adamc@859 37 } FCGI_Output;
adamc@859 38
adamc@859 39 typedef struct {
adamc@859 40 char buf[sizeof(FCGI_Record) + 255];
adamc@859 41 int available, used, sock;
adamc@859 42 } FCGI_Input;
adamc@859 43
adamc@859 44 static FCGI_Output *fastcgi_output() {
adamc@859 45 FCGI_Output *o = malloc(sizeof(FCGI_Output));
adamc@859 46
adamc@859 47 o->r.version = FCGI_VERSION_1;
adamc@859 48 o->r.paddingLength = 0;
adamc@859 49 o->r.reserved = 0;
adamc@859 50
adamc@859 51 return o;
adamc@859 52 }
adamc@859 53
adamc@859 54 static FCGI_Input *fastcgi_input() {
adamc@859 55 FCGI_Input *i = malloc(sizeof(FCGI_Input));
adamc@859 56
adamc@859 57 i->available = i->used = 0;
adamc@859 58
adamc@859 59 return i;
adamc@859 60 }
adamc@859 61
adamc@859 62 static void fastcgi_input_reset(FCGI_Input *i) {
adamc@859 63 i->available = i->used = 0;
adamc@859 64 }
adamc@859 65
adamc@859 66 static int fastcgi_send(FCGI_Output *o,
adamc@859 67 unsigned char type,
adamc@859 68 unsigned short requestId,
adamc@859 69 unsigned short contentLength) {
adamc@859 70 o->r.type = type;
adamc@859 71 o->r.requestIdB1 = requestId >> 8;
adamc@859 72 o->r.requestIdB0 = requestId & 255;
adamc@859 73 o->r.contentLengthB1 = contentLength >> 8;
adamc@859 74 o->r.contentLengthB0 = contentLength & 255;
adamc@859 75 return uw_really_send(o->sock, &o->r, sizeof(o->r) - (65535 - contentLength));
adamc@859 76 }
adamc@859 77
adamc@859 78 #define LATEST(i) ((FCGI_Record *)(i->buf + i->used))
adamc@859 79
adamc@859 80 static FCGI_Record *fastcgi_recv(FCGI_Input *i) {
adamc@859 81 while (1) {
adamc@859 82 ssize_t n;
adamc@859 83
adamc@859 84 if (i->available >= i->used + sizeof(FCGI_Record) - 65535
adamc@859 85 && i->available >= i->used + sizeof(FCGI_Record) - 65535
adamc@859 86 + ((LATEST(i)->contentLengthB1 << 8) & LATEST(i)->contentLengthB0)
adamc@859 87 + LATEST(i)->paddingLength) {
adamc@859 88 FCGI_Record *r = LATEST(i);
adamc@859 89
adamc@859 90 i->used += sizeof(FCGI_Record) - 65535
adamc@859 91 + ((LATEST(i)->contentLengthB1 << 8) & LATEST(i)->contentLengthB0)
adamc@859 92 + LATEST(i)->paddingLength;
adamc@859 93
adamc@859 94 return r;
adamc@859 95 }
adamc@859 96
adamc@859 97 if (i->used > 0) {
adamc@859 98 memmove(i->buf, i->buf + i->used, i->available - i->used);
adamc@859 99 i->available -= i->used;
adamc@859 100 i->used = 0;
adamc@859 101 }
adamc@859 102
adamc@859 103 n = recv(i->sock, i->buf + i->available, sizeof(i->buf) - i->available, 0);
adamc@859 104
adamc@859 105 if (n <= 0)
adamc@859 106 return NULL;
adamc@859 107
adamc@859 108 i->available += n;
adamc@859 109 }
adamc@859 110 }
adamc@859 111
adamc@859 112 static char *get_header(void *data, const char *h) {
adamc@859 113 return NULL;
adamc@859 114 }
adamc@859 115
adamc@859 116 static void on_success(uw_context ctx) { }
adamc@859 117
adamc@859 118 static void on_failure(uw_context ctx) {
adamc@859 119 uw_write_header(ctx, "Status: 500 Internal Server Error\r\n");
adamc@859 120 }
adamc@859 121
adamc@859 122 static void write_stderr(FCGI_Output *o, const char *fmt, ...) {
adamc@859 123 int len;
adamc@859 124 va_list ap;
adamc@859 125 va_start(ap, fmt);
adamc@859 126
adamc@859 127 len = vsnprintf(o->r.contentData, 65535, fmt, ap);
adamc@859 128 if (len < 0)
adamc@859 129 fprintf(stderr, "vsnprintf() failed in log_error().\n");
adamc@859 130 else if (fastcgi_send(o, FCGI_STDERR, FCGI_NULL_REQUEST_ID, len))
adamc@859 131 fprintf(stderr, "fastcgi_send() failed in log_error().\n");
adamc@859 132 }
adamc@859 133
adamc@859 134 static void log_error(void *data, const char *fmt, ...) {
adamc@859 135 FCGI_Output *o = (FCGI_Output *)data;
adamc@859 136 va_list ap;
adamc@859 137 va_start(ap, fmt);
adamc@859 138
adamc@859 139 if (o) {
adamc@859 140 int len = vsnprintf(o->r.contentData, 65535, fmt, ap);
adamc@859 141 if (len < 0)
adamc@859 142 fprintf(stderr, "vsnprintf() failed in log_error().\n");
adamc@859 143 else if (fastcgi_send(o, FCGI_STDERR, FCGI_NULL_REQUEST_ID, len))
adamc@859 144 fprintf(stderr, "fastcgi_send() failed in log_error().\n");
adamc@859 145 } else
adamc@859 146 vfprintf(stderr, fmt, ap);
adamc@859 147 }
adamc@859 148
adamc@859 149 static void log_debug(void *data, const char *fmt, ...) {
adamc@859 150 }
adamc@859 151
adamc@859 152 static void *worker(void *data) {
adamc@859 153 int me = *(int *)data;
adamc@859 154 FCGI_Input *in = fastcgi_input();
adamc@859 155 FCGI_Output *out = fastcgi_output();
adamc@859 156 uw_context ctx = uw_request_new_context(out, log_error, log_debug);
adamc@859 157 uw_request_context rc = uw_new_request_context();
adamc@859 158
adamc@859 159 while (1) {
adamc@859 160 FCGI_Record *r;
adamc@859 161
adamc@859 162 in->sock = out->sock = uw_dequeue();
adamc@859 163
adamc@859 164 if (!(r = fastcgi_recv(in))) {
adamc@859 165 fprintf(stderr, "Error receiving initial message\n");
adamc@859 166 return NULL;
adamc@859 167 }
adamc@859 168
adamc@859 169 if (r->type != FCGI_BEGIN_REQUEST) {
adamc@859 170 write_stderr(out, "First message is not BEGIN_REQUEST\n");
adamc@859 171 goto done;
adamc@859 172 } else if (((FCGI_BeginRequestBody *)&r->contentData)->roleB0 != FCGI_RESPONDER) {
adamc@859 173 write_stderr(out, "First message is not BEGIN_REQUEST\n");
adamc@859 174 goto done;
adamc@859 175 }
adamc@859 176
adamc@859 177 if (!(r = fastcgi_recv(in))) {
adamc@859 178 fprintf(stderr, "Error receiving second message\n");
adamc@859 179 return NULL;
adamc@859 180 }
adamc@859 181 write_stderr(out, "Next message code: %d\n", r->type);
adamc@859 182
adamc@859 183 done:
adamc@859 184 close(in->sock);
adamc@859 185 fastcgi_input_reset(in);
adamc@859 186 uw_reset(ctx);
adamc@859 187 }
adamc@859 188 }
adamc@859 189
adamc@859 190 static void help(char *cmd) {
adamc@859 191 printf("Usage: %s [-t <thread-count>]\n", cmd);
adamc@859 192 }
adamc@859 193
adamc@859 194 static void sigint(int signum) {
adamc@859 195 printf("Exiting....\n");
adamc@859 196 exit(0);
adamc@859 197 }
adamc@859 198
adamc@859 199 static loggers ls = {NULL, log_error, log_debug};
adamc@859 200
adamc@859 201 int main(int argc, char *argv[]) {
adamc@859 202 // The skeleton for this function comes from Beej's sockets tutorial.
adamc@859 203 struct sockaddr_in their_addr; // connector's address information
adamc@859 204 int sin_size, yes = 1;
adamc@859 205 int nthreads = 1, i, *names, opt;
adamc@859 206 char *fwsa = getenv("FCGI_WEB_SERVER_ADDRS"), *nthreads_s = getenv("URWEB_NUM_THREADS");
adamc@859 207
adamc@859 208 if (nthreads_s) {
adamc@859 209 nthreads = atoi(nthreads_s);
adamc@859 210 if (nthreads <= 0) {
adamc@859 211 fprintf(stderr, "Bad URWEB_NUM_THREADS value\n");
adamc@859 212 return 1;
adamc@859 213 }
adamc@859 214 }
adamc@859 215
adamc@859 216 signal(SIGINT, sigint);
adamc@859 217 signal(SIGPIPE, SIG_IGN);
adamc@859 218 signal(SIGUSR1, sigint);
adamc@859 219 signal(SIGTERM, sigint);
adamc@859 220
adamc@859 221 while ((opt = getopt(argc, argv, "ht:")) != -1) {
adamc@859 222 switch (opt) {
adamc@859 223 case '?':
adamc@859 224 fprintf(stderr, "Unknown command-line option");
adamc@859 225 help(argv[0]);
adamc@859 226 return 1;
adamc@859 227
adamc@859 228 case 'h':
adamc@859 229 help(argv[0]);
adamc@859 230 return 0;
adamc@859 231
adamc@859 232 case 't':
adamc@859 233 nthreads = atoi(optarg);
adamc@859 234 if (nthreads <= 0) {
adamc@859 235 fprintf(stderr, "Invalid thread count\n");
adamc@859 236 help(argv[0]);
adamc@859 237 return 1;
adamc@859 238 }
adamc@859 239 break;
adamc@859 240
adamc@859 241 default:
adamc@859 242 fprintf(stderr, "Unexpected getopt() behavior\n");
adamc@859 243 return 1;
adamc@859 244 }
adamc@859 245 }
adamc@859 246
adamc@859 247 uw_request_init(NULL, log_error, log_debug);
adamc@859 248
adamc@859 249 names = calloc(nthreads, sizeof(int));
adamc@859 250
adamc@859 251 sin_size = sizeof their_addr;
adamc@859 252
adamc@859 253 {
adamc@859 254 pthread_t thread;
adamc@859 255 int name;
adamc@859 256
adamc@859 257 if (pthread_create(&thread, NULL, client_pruner, &ls)) {
adamc@859 258 fprintf(stderr, "Error creating pruner thread\n");
adamc@859 259 return 1;
adamc@859 260 }
adamc@859 261 }
adamc@859 262
adamc@859 263 for (i = 0; i < nthreads; ++i) {
adamc@859 264 pthread_t thread;
adamc@859 265 names[i] = i;
adamc@859 266 if (pthread_create(&thread, NULL, worker, &names[i])) {
adamc@859 267 fprintf(stderr, "Error creating worker thread #%d\n", i);
adamc@859 268 return 1;
adamc@859 269 }
adamc@859 270 }
adamc@859 271
adamc@859 272 while (1) {
adamc@859 273 int new_fd = accept(FCGI_LISTENSOCK_FILENO, (struct sockaddr *)&their_addr, &sin_size);
adamc@859 274
adamc@859 275 if (new_fd < 0) {
adamc@859 276 fprintf(stderr, "Socket accept failed\n");
adamc@859 277 return 1;
adamc@859 278 }
adamc@859 279
adamc@859 280 if (fwsa) {
adamc@859 281 char host[100], matched = 0;
adamc@859 282 char *ips, *sep;
adamc@859 283
adamc@859 284 if (getnameinfo((struct sockaddr *)&their_addr, sin_size, host, sizeof host, NULL, 0, NI_NUMERICHOST)) {
adamc@859 285 fprintf(stderr, "Remote IP determination failed\n");
adamc@859 286 return 1;
adamc@859 287 }
adamc@859 288
adamc@859 289 for (ips = fwsa; sep = strchr(ips, ','); ips = sep+1) {
adamc@859 290 if (!strncmp(ips, host, sep - ips)) {
adamc@859 291 matched = 1;
adamc@859 292 break;
adamc@859 293 }
adamc@859 294 }
adamc@859 295
adamc@859 296 if (!matched && strcmp(ips, host)) {
adamc@859 297 fprintf(stderr, "Remote address is not in FCGI_WEB_SERVER_ADDRS");
adamc@859 298 return 1;
adamc@859 299 }
adamc@859 300 }
adamc@859 301
adamc@859 302 uw_enqueue(new_fd);
adamc@859 303 }
adamc@859 304 }