Mercurial > urweb
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; |