comparison src/c/fastcgi.c @ 860:a738002d5b4d

Serving Hello via FastCGI
author Adam Chlipala <adamc@hcoop.net>
date Sat, 27 Jun 2009 14:44:00 -0400
parents 60240acd15b9
children bd153951c794
comparison
equal deleted inserted replaced
859:60240acd15b9 860:a738002d5b4d
63 i->available = i->used = 0; 63 i->available = i->used = 0;
64 } 64 }
65 65
66 static int fastcgi_send(FCGI_Output *o, 66 static int fastcgi_send(FCGI_Output *o,
67 unsigned char type, 67 unsigned char type,
68 unsigned short requestId,
69 unsigned short contentLength) { 68 unsigned short contentLength) {
70 o->r.type = type; 69 o->r.type = type;
71 o->r.requestIdB1 = requestId >> 8; 70 o->r.requestIdB1 = o->r.requestIdB0 = 0;
72 o->r.requestIdB0 = requestId & 255;
73 o->r.contentLengthB1 = contentLength >> 8; 71 o->r.contentLengthB1 = contentLength >> 8;
74 o->r.contentLengthB0 = contentLength & 255; 72 o->r.contentLengthB0 = contentLength & 255;
75 return uw_really_send(o->sock, &o->r, sizeof(o->r) - (65535 - contentLength)); 73 return uw_really_send(o->sock, &o->r, sizeof(o->r) - (65535 - contentLength));
76 } 74 }
77 75
81 while (1) { 79 while (1) {
82 ssize_t n; 80 ssize_t n;
83 81
84 if (i->available >= i->used + sizeof(FCGI_Record) - 65535 82 if (i->available >= i->used + sizeof(FCGI_Record) - 65535
85 && i->available >= i->used + sizeof(FCGI_Record) - 65535 83 && i->available >= i->used + sizeof(FCGI_Record) - 65535
86 + ((LATEST(i)->contentLengthB1 << 8) & LATEST(i)->contentLengthB0) 84 + ((LATEST(i)->contentLengthB1 << 8) | LATEST(i)->contentLengthB0)
87 + LATEST(i)->paddingLength) { 85 + LATEST(i)->paddingLength) {
88 FCGI_Record *r = LATEST(i); 86 FCGI_Record *r = LATEST(i);
89 87
90 i->used += sizeof(FCGI_Record) - 65535 88 i->used += sizeof(FCGI_Record) - 65535
91 + ((LATEST(i)->contentLengthB1 << 8) & LATEST(i)->contentLengthB0) 89 + ((LATEST(i)->contentLengthB1 << 8) | LATEST(i)->contentLengthB0)
92 + LATEST(i)->paddingLength; 90 + LATEST(i)->paddingLength;
93 91
94 return r; 92 return r;
95 } 93 }
96 94
107 105
108 i->available += n; 106 i->available += n;
109 } 107 }
110 } 108 }
111 109
112 static char *get_header(void *data, const char *h) {
113 return NULL;
114 }
115
116 static void on_success(uw_context ctx) { } 110 static void on_success(uw_context ctx) { }
117 111
118 static void on_failure(uw_context ctx) { 112 static void on_failure(uw_context ctx) {
119 uw_write_header(ctx, "Status: 500 Internal Server Error\r\n"); 113 uw_write_header(ctx, "Status: 500 Internal Server Error\r\n");
114 }
115
116 static int write_stdout(void *data, char *buf, size_t len) {
117 FCGI_Output *o = (FCGI_Output *)data;
118 while (len > 0) {
119 size_t len2 = len;
120 if (len2 > 65535)
121 len2 = 65535;
122 memcpy(o->r.contentData, buf, len2);
123 if (fastcgi_send(o, FCGI_STDOUT, len2)) {
124 fprintf(stderr, "fastcgi_send() failed in write_stdout().\n");
125 return -1;
126 }
127 buf += len2;
128 len -= len2;
129 }
130
131 return 0;
120 } 132 }
121 133
122 static void write_stderr(FCGI_Output *o, const char *fmt, ...) { 134 static void write_stderr(FCGI_Output *o, const char *fmt, ...) {
123 int len; 135 int len;
124 va_list ap; 136 va_list ap;
125 va_start(ap, fmt); 137 va_start(ap, fmt);
126 138
127 len = vsnprintf(o->r.contentData, 65535, fmt, ap); 139 len = vsnprintf(o->r.contentData, 65535, fmt, ap);
128 if (len < 0) 140 if (len < 0)
129 fprintf(stderr, "vsnprintf() failed in log_error().\n"); 141 fprintf(stderr, "vsnprintf() failed in write_stderr().\n");
130 else if (fastcgi_send(o, FCGI_STDERR, FCGI_NULL_REQUEST_ID, len)) 142 else if (fastcgi_send(o, FCGI_STDERR, len))
131 fprintf(stderr, "fastcgi_send() failed in log_error().\n"); 143 fprintf(stderr, "fastcgi_send() failed in write_stderr().\n");
144 }
145
146 static void close_stream(FCGI_Output *o, unsigned char type) {
147 if (fastcgi_send(o, type, 0))
148 fprintf(stderr, "fastcgi_send() failed in close_stream().\n");
132 } 149 }
133 150
134 static void log_error(void *data, const char *fmt, ...) { 151 static void log_error(void *data, const char *fmt, ...) {
135 FCGI_Output *o = (FCGI_Output *)data; 152 FCGI_Output *o = (FCGI_Output *)data;
136 va_list ap; 153 va_list ap;
138 155
139 if (o) { 156 if (o) {
140 int len = vsnprintf(o->r.contentData, 65535, fmt, ap); 157 int len = vsnprintf(o->r.contentData, 65535, fmt, ap);
141 if (len < 0) 158 if (len < 0)
142 fprintf(stderr, "vsnprintf() failed in log_error().\n"); 159 fprintf(stderr, "vsnprintf() failed in log_error().\n");
143 else if (fastcgi_send(o, FCGI_STDERR, FCGI_NULL_REQUEST_ID, len)) 160 else if (fastcgi_send(o, FCGI_STDERR, len))
144 fprintf(stderr, "fastcgi_send() failed in log_error().\n"); 161 fprintf(stderr, "fastcgi_send() failed in log_error().\n");
145 } else 162 } else
146 vfprintf(stderr, fmt, ap); 163 vfprintf(stderr, fmt, ap);
147 } 164 }
148 165
149 static void log_debug(void *data, const char *fmt, ...) { 166 static void log_debug(void *data, const char *fmt, ...) {
167 }
168
169 static int read_funny_len(char **buf, int *len) {
170 if (*len <= 0)
171 return -1;
172
173 if ((*buf)[0] >> 7 == 0) {
174 int r = (*buf)[0];
175 ++*buf;
176 --*len;
177 return r;
178 }
179 else if (*len < 4)
180 return -1;
181 else {
182 int r = (((*buf)[3] & 0x7f) << 24) + ((*buf)[2] << 16) + ((*buf)[1] << 8) + (*buf)[0];
183 *buf += 4;
184 *len -= 4;
185 return r;
186 }
187 }
188
189 typedef struct {
190 char *name, *value;
191 unsigned name_len, value_len;
192 } nvp;
193
194 static char *search_nvps(nvp *nvps, const char *h) {
195 for (; nvps->name; ++nvps)
196 if (!strcmp(h, nvps->name))
197 return nvps->value;
198
199 return NULL;
200 }
201
202 typedef struct {
203 nvp *nvps;
204 char *uppercased;
205 int n_nvps, uppercased_len;
206 } headers;
207
208 static char *get_header(void *data, const char *h) {
209 headers *hs = (headers *)data;
210 size_t len = strlen(h);
211 char *s, *r;
212 const char *saved_h = h;
213
214 if (len > hs->uppercased_len) {
215 hs->uppercased_len = len;
216 hs->uppercased = realloc(hs->uppercased, len + 6);
217 }
218
219 strcpy(hs->uppercased, "HTTP_");
220 for (s = hs->uppercased+5; *h; ++h)
221 *s++ = *h == '-' ? '_' : toupper(*h);
222 *s = 0;
223
224 if (!strcasecmp(saved_h, "Content-length")
225 || !strcasecmp(saved_h, "Content-type"))
226 return search_nvps(hs->nvps, hs->uppercased + 5);
227 else
228 return search_nvps(hs->nvps, hs->uppercased);
229 }
230
231 static int read_nvp(char *buf, int len, nvp *nv) {
232 int nameLength, valueLength;
233
234 if ((nameLength = read_funny_len(&buf, &len)) < 0)
235 return -1;
236 if ((valueLength = read_funny_len(&buf, &len)) < 0)
237 return -1;
238 if (len < nameLength + valueLength)
239 return -1;
240
241 if (nameLength+1 > nv->name_len) {
242 nv->name_len = nameLength+1;
243 nv->name = realloc(nv->name, nv->name_len);
244 }
245 if (valueLength+1 > nv->value_len) {
246 nv->value_len = valueLength+1;
247 nv->value = realloc(nv->value, nv->value_len);
248 }
249
250 memcpy(nv->name, buf, nameLength);
251 nv->name[nameLength] = 0;
252
253 memcpy(nv->value, buf + nameLength, valueLength);
254 nv->value[valueLength] = 0;
255
256 return 0;
150 } 257 }
151 258
152 static void *worker(void *data) { 259 static void *worker(void *data) {
153 int me = *(int *)data; 260 int me = *(int *)data;
154 FCGI_Input *in = fastcgi_input(); 261 FCGI_Input *in = fastcgi_input();
155 FCGI_Output *out = fastcgi_output(); 262 FCGI_Output *out = fastcgi_output();
156 uw_context ctx = uw_request_new_context(out, log_error, log_debug); 263 uw_context ctx = uw_request_new_context(out, log_error, log_debug);
157 uw_request_context rc = uw_new_request_context(); 264 uw_request_context rc = uw_new_request_context();
265 headers hs;
266 size_t body_size = 0;
267 char *body = malloc(0);
268 size_t path_size = 0;
269 char *path_buf = malloc(0);
270
271 hs.uppercased = malloc(0);
272 hs.uppercased_len = 0;
273 hs.nvps = malloc(sizeof(nvp));
274 hs.n_nvps = 1;
158 275
159 while (1) { 276 while (1) {
160 FCGI_Record *r; 277 FCGI_Record *r;
278 size_t used_nvps = 0;
279 int body_len, body_read;
280 char *s;
281 char *method, *path, *path_info, *query_string;
161 282
162 in->sock = out->sock = uw_dequeue(); 283 in->sock = out->sock = uw_dequeue();
163 284
164 if (!(r = fastcgi_recv(in))) { 285 if (!(r = fastcgi_recv(in))) {
165 fprintf(stderr, "Error receiving initial message\n"); 286 fprintf(stderr, "Error receiving initial message\n");
166 return NULL; 287 goto done;
167 } 288 }
168 289
169 if (r->type != FCGI_BEGIN_REQUEST) { 290 if (r->type != FCGI_BEGIN_REQUEST) {
170 write_stderr(out, "First message is not BEGIN_REQUEST\n"); 291 write_stderr(out, "First message is not BEGIN_REQUEST\n");
171 goto done; 292 goto done;
172 } else if (((FCGI_BeginRequestBody *)&r->contentData)->roleB0 != FCGI_RESPONDER) { 293 } else if (((FCGI_BeginRequestBody *)&r->contentData)->roleB0 != FCGI_RESPONDER) {
173 write_stderr(out, "First message is not BEGIN_REQUEST\n"); 294 write_stderr(out, "First message is not BEGIN_REQUEST\n");
174 goto done; 295 goto done;
175 } 296 }
176 297
177 if (!(r = fastcgi_recv(in))) { 298 while (1) {
178 fprintf(stderr, "Error receiving second message\n"); 299 char *buf;
179 return NULL; 300
180 } 301 if (!(r = fastcgi_recv(in))) {
181 write_stderr(out, "Next message code: %d\n", r->type); 302 write_stderr(out, "Error receiving environment variables\n");
303 goto done;
304 }
305
306 if (r->type != FCGI_PARAMS) {
307 write_stderr(out, "Expected FCGI_PARAMS but got %d\n", r->type);
308 goto done;
309 }
310
311 if (r->contentLengthB1 == 0 && r->contentLengthB0 == 0)
312 break;
313
314 if (used_nvps == hs.n_nvps-1) {
315 ++hs.n_nvps;
316 hs.nvps = realloc(hs.nvps, hs.n_nvps * sizeof(nvp));
317 hs.nvps[used_nvps].name = malloc(0);
318 hs.nvps[used_nvps].value = malloc(0);
319 hs.nvps[used_nvps].name_len = 0;
320 hs.nvps[used_nvps].value_len = 0;
321 }
322
323 if (read_nvp(r->contentData, (r->contentLengthB1 << 8) | r->contentLengthB0, &hs.nvps[used_nvps]) < 0) {
324 write_stderr(out, "Error reading FCGI_PARAMS name-value pair\n");
325 goto done;
326 }
327
328 ++used_nvps;
329 }
330
331 hs.nvps[used_nvps].name = NULL;
332
333 if (s = get_header(&hs, "Content-Length")) {
334 body_len = atoi(s);
335 if (body_len < 0) {
336 write_stderr(out, "Invalid Content-Length\n");
337 goto done;
338 }
339 } else
340 body_len = 0;
341
342 if (body_len > body_size) {
343 body_size = body_len;
344 body = realloc(body, body_size);
345 }
346
347 for (body_read = 0; body_read < body_len; ) {
348 char *buf;
349 int this_len;
350
351 if (!(r = fastcgi_recv(in))) {
352 write_stderr(out, "Error receiving STDIN\n");
353 goto done;
354 }
355
356 if (r->type != FCGI_STDIN) {
357 write_stderr(out, "Expected FCGI_STDIN but got %d\n", r->type);
358 goto done;
359 }
360
361 if (r->contentLengthB1 == 0 && r->contentLengthB0 == 0) {
362 write_stderr(out, "End of STDIN\n");
363 break;
364 }
365
366 this_len = (r->contentLengthB1 << 8) | r->contentLengthB0;
367
368 if (body_read + this_len > body_len) {
369 write_stderr(out, "Too much STDIN\n");
370 goto done;
371 }
372
373 memcpy(&body[body_read], r->contentData, this_len);
374 body_read += this_len;
375 }
376
377 if (!(method = search_nvps(hs.nvps, "REQUEST_METHOD"))) {
378 write_stderr(out, "REQUEST_METHOD not set\n");
379 goto done;
380 }
381
382 if (!(path = search_nvps(hs.nvps, "SCRIPT_NAME"))) {
383 write_stderr(out, "SCRIPT_NAME not set\n");
384 goto done;
385 }
386
387 if (path_info = search_nvps(hs.nvps, "PATH_INFO")) {
388 int len1 = strlen(path), len2 = strlen(path_info);
389 int len = len1 + len2 + 1;
390
391 if (len > path_size) {
392 path_size = len;
393 path_buf = realloc(path_buf, path_size);
394 }
395
396 sprintf(path_buf, "%s%s", path, path_info);
397 path = path_buf;
398 }
399
400 if (!(query_string = search_nvps(hs.nvps, "QUERY_STRING")))
401 query_string = "";
402
403 uw_set_headers(ctx, get_header, &hs);
404 {
405 request_result rr;
406 FCGI_EndRequestBody *erb = (FCGI_EndRequestBody *)out->r.contentData;
407
408 rr = uw_request(rc, ctx, method, path, query_string, body, body_read,
409 on_success, on_failure,
410 out, log_error, log_debug,
411 in->sock);
412
413 if (rr == KEEP_OPEN)
414 goto done2;
415
416 uw_output(ctx, write_stdout, out);
417 close_stream(out, FCGI_STDOUT);
418 close_stream(out, FCGI_STDERR);
419
420 if (rr == SERVED)
421 erb->appStatusB3 = erb->appStatusB2 = erb->appStatusB1 = erb->appStatusB0 = 0;
422 else
423 erb->appStatusB3 = erb->appStatusB2 = erb->appStatusB1 = erb->appStatusB0 = 0xFF;
424
425 erb->protocolStatus = FCGI_REQUEST_COMPLETE;
426 fastcgi_send(out, FCGI_END_REQUEST, sizeof(FCGI_EndRequestBody));
427 }
182 428
183 done: 429 done:
184 close(in->sock); 430 close(in->sock);
431 done2:
185 fastcgi_input_reset(in); 432 fastcgi_input_reset(in);
186 uw_reset(ctx); 433 uw_reset(ctx);
187 } 434 }
188 } 435 }
189 436
242 fprintf(stderr, "Unexpected getopt() behavior\n"); 489 fprintf(stderr, "Unexpected getopt() behavior\n");
243 return 1; 490 return 1;
244 } 491 }
245 } 492 }
246 493
494 uw_set_on_success("");
247 uw_request_init(NULL, log_error, log_debug); 495 uw_request_init(NULL, log_error, log_debug);
248 496
249 names = calloc(nthreads, sizeof(int)); 497 names = calloc(nthreads, sizeof(int));
250 498
251 sin_size = sizeof their_addr; 499 sin_size = sizeof their_addr;