comparison src/c/driver.c @ 853:19fdeef40ada

Factor out common request functionality, in preparation for supporting different protocols
author Adam Chlipala <adamc@hcoop.net>
date Tue, 23 Jun 2009 14:05:12 -0400
parents 5f49a6b759cb
children 158d980889ac
comparison
equal deleted inserted replaced
852:4d4c62d95b9c 853:19fdeef40ada
12 #include <pthread.h> 12 #include <pthread.h>
13 13
14 #include <mhash.h> 14 #include <mhash.h>
15 15
16 #include "urweb.h" 16 #include "urweb.h"
17 #include "request.h"
17 18
18 int uw_backlog = 10; 19 int uw_backlog = 10;
19 int uw_bufsize = 1024;
20 20
21 typedef struct node { 21 typedef struct node {
22 int fd; 22 int fd;
23 struct node *next; 23 struct node *next;
24 } *node; 24 } *node;
52 } 52 }
53 53
54 static pthread_mutex_t queue_mutex = PTHREAD_MUTEX_INITIALIZER; 54 static pthread_mutex_t queue_mutex = PTHREAD_MUTEX_INITIALIZER;
55 static pthread_cond_t queue_cond = PTHREAD_COND_INITIALIZER; 55 static pthread_cond_t queue_cond = PTHREAD_COND_INITIALIZER;
56 56
57 #define MAX_RETRIES 5
58
59 static int try_rollback(uw_context ctx) {
60 int r = uw_rollback(ctx);
61
62 if (r) {
63 printf("Error running SQL ROLLBACK\n");
64 uw_reset(ctx);
65 uw_write(ctx, "HTTP/1.1 500 Internal Server Error\n\r");
66 uw_write(ctx, "Content-type: text/plain\r\n\r\n");
67 uw_write(ctx, "Error running SQL ROLLBACK\n");
68 }
69
70 return r;
71 }
72
73 static uw_context new_context() {
74 uw_context ctx = uw_init();
75 int retries_left = MAX_RETRIES;
76
77 while (1) {
78 failure_kind fk = uw_begin_init(ctx);
79
80 if (fk == SUCCESS) {
81 printf("Database connection initialized.\n");
82 break;
83 } else if (fk == BOUNDED_RETRY) {
84 if (retries_left) {
85 printf("Initialization error triggers bounded retry: %s\n", uw_error_message(ctx));
86 --retries_left;
87 } else {
88 printf("Fatal initialization error (out of retries): %s\n", uw_error_message(ctx));
89 uw_free(ctx);
90 return NULL;
91 }
92 } else if (fk == UNLIMITED_RETRY)
93 printf("Initialization error triggers unlimited retry: %s\n", uw_error_message(ctx));
94 else if (fk == FATAL) {
95 printf("Fatal initialization error: %s\n", uw_error_message(ctx));
96 uw_free(ctx);
97 return NULL;
98 } else {
99 printf("Unknown uw_begin_init return code!\n");
100 uw_free(ctx);
101 return NULL;
102 }
103 }
104
105 return ctx;
106 }
107
108 #define KEYSIZE 16
109 #define PASSSIZE 4
110
111 #define HASH_ALGORITHM MHASH_SHA256
112 #define HASH_BLOCKSIZE 32
113 #define KEYGEN_ALGORITHM KEYGEN_MCRYPT
114
115 int uw_hash_blocksize = HASH_BLOCKSIZE;
116
117 static int password[PASSSIZE];
118 static unsigned char private_key[KEYSIZE];
119
120 static void init_crypto() {
121 KEYGEN kg = {{HASH_ALGORITHM, HASH_ALGORITHM}};
122 int i;
123
124 assert(mhash_get_block_size(HASH_ALGORITHM) == HASH_BLOCKSIZE);
125
126 for (i = 0; i < PASSSIZE; ++i)
127 password[i] = rand();
128
129 if (mhash_keygen_ext(KEYGEN_ALGORITHM, kg,
130 private_key, sizeof(private_key),
131 (unsigned char*)password, sizeof(password)) < 0) {
132 printf("Key generation failed\n");
133 exit(1);
134 }
135 }
136
137 void uw_sign(const char *in, char *out) {
138 MHASH td;
139
140 td = mhash_hmac_init(HASH_ALGORITHM, private_key, sizeof(private_key),
141 mhash_get_hash_pblock(HASH_ALGORITHM));
142
143 mhash(td, in, strlen(in));
144 if (mhash_hmac_deinit(td, out) < 0)
145 printf("Signing failed");
146 }
147
148 static void *worker(void *data) { 57 static void *worker(void *data) {
149 int me = *(int *)data, retries_left = MAX_RETRIES; 58 int me = *(int *)data;
150 uw_context ctx = new_context(); 59 uw_context ctx = uw_request_new_context();
151 size_t buf_size = 2; 60 size_t buf_size = 2;
152 char *buf = malloc(buf_size); 61 char *buf = malloc(buf_size);
153 size_t path_copy_size = 0; 62 uw_request_context rc = uw_new_request_context();
154 char *path_copy = malloc(path_copy_size);
155 63
156 while (1) { 64 while (1) {
157 char *back = buf, *s, *post; 65 char *back = buf;
158 int sock, dont_close = 0; 66 int sock;
159 67
160 pthread_mutex_lock(&queue_mutex); 68 pthread_mutex_lock(&queue_mutex);
161 while (empty()) 69 while (empty())
162 pthread_cond_wait(&queue_cond, &queue_mutex); 70 pthread_cond_wait(&queue_cond, &queue_mutex);
163 sock = dequeue(); 71 sock = dequeue();
164 pthread_mutex_unlock(&queue_mutex); 72 pthread_mutex_unlock(&queue_mutex);
165 73
166 printf("Handling connection with thread #%d.\n", me); 74 printf("Handling connection with thread #%d.\n", me);
167 75
168 while (1) { 76 while (1) {
169 unsigned retries_left = MAX_RETRIES;
170 int r; 77 int r;
78 char *s1, *s2;
171 79
172 if (back - buf == buf_size - 1) { 80 if (back - buf == buf_size - 1) {
173 char *new_buf; 81 char *new_buf;
174 buf_size *= 2; 82 buf_size *= 2;
175 new_buf = realloc(buf, buf_size); 83 new_buf = realloc(buf, buf_size);
187 if (r == 0) { 95 if (r == 0) {
188 printf("Connection closed.\n"); 96 printf("Connection closed.\n");
189 break; 97 break;
190 } 98 }
191 99
192 //printf("Received %d bytes.\n", r);
193
194 back += r; 100 back += r;
195 *back = 0; 101 *back = 0;
196 102
197 if (s = strstr(buf, "\r\n\r\n")) { 103 if ((s1 = strstr(buf, "\r\n\r\n"))) {
198 failure_kind fk; 104 request_result rr;
199 int is_post = 0, do_normal_send = 1; 105
200 char *boundary = NULL; 106 if ((s2 = strcasestr(buf, "\r\nContent-Length: ")) && s2 < s1) {
201 size_t boundary_len; 107 int clen;
202 char *cmd, *path, *headers, *inputs, *after_headers; 108
203 109 if (sscanf(s2 + 18, "%d\r\n", &clen) != 1) {
204 //printf("All: %s\n", buf); 110 fprintf(stderr, "Malformed Content-Length header\n");
205
206 s[2] = 0;
207 after_headers = s + 4;
208
209 if (!(s = strstr(buf, "\r\n"))) {
210 fprintf(stderr, "No newline in buf\n");
211 break;
212 }
213
214 *s = 0;
215 headers = s + 2;
216 cmd = s = buf;
217
218 //printf("Read: %s\n", buf);
219
220 if (!strsep(&s, " ")) {
221 fprintf(stderr, "No first space in HTTP command\n");
222 break;
223 }
224
225 uw_set_headers(ctx, headers);
226
227 if (!strcmp(cmd, "POST")) {
228 char *clen_s = uw_Basis_requestHeader(ctx, "Content-length");
229 if (!clen_s) {
230 fprintf(stderr, "No Content-length with POST\n");
231 goto done;
232 }
233 int clen = atoi(clen_s);
234 if (clen < 0) {
235 fprintf(stderr, "Negative Content-length with POST\n");
236 goto done;
237 }
238
239 while (back - after_headers < clen) {
240 if (back - buf == buf_size - 1) {
241 char *new_buf;
242 buf_size *= 2;
243 new_buf = realloc(buf, buf_size);
244
245 back = new_buf + (back - buf);
246 headers = new_buf + (headers - buf);
247 uw_headers_moved(ctx, headers);
248 after_headers = new_buf + (after_headers - buf);
249 s = new_buf + (s - buf);
250
251 buf = new_buf;
252 }
253
254 r = recv(sock, back, buf_size - 1 - (back - buf), 0);
255
256 if (r < 0) {
257 fprintf(stderr, "Recv failed\n");
258 goto done;
259 }
260
261 if (r == 0) {
262 printf("Connection closed.\n");
263 goto done;
264 }
265
266 back += r;
267 *back = 0;
268 }
269
270 is_post = 1;
271
272 clen_s = uw_Basis_requestHeader(ctx, "Content-type");
273 if (clen_s && !strncasecmp(clen_s, "multipart/form-data", 19)) {
274 if (strncasecmp(clen_s + 19, "; boundary=", 11)) {
275 fprintf(stderr, "Bad multipart boundary spec");
276 break;
277 }
278
279 boundary = clen_s + 28;
280 boundary[0] = '-';
281 boundary[1] = '-';
282 boundary_len = strlen(boundary);
283 }
284 } else if (strcmp(cmd, "GET")) {
285 fprintf(stderr, "Not ready for non-GET/POST command: %s\n", cmd);
286 break;
287 }
288
289 path = s;
290 if (!strsep(&s, " ")) {
291 fprintf(stderr, "No second space in HTTP command\n");
292 break;
293 }
294
295 if (!strcmp(path, "/.msgs")) {
296 char *id = uw_Basis_requestHeader(ctx, "UrWeb-Client");
297 char *pass = uw_Basis_requestHeader(ctx, "UrWeb-Pass");
298
299 if (id && pass) {
300 unsigned idn = atoi(id);
301 uw_client_connect(idn, atoi(pass), sock);
302 dont_close = 1;
303 fprintf(stderr, "Processed request for messages by client %u\n\n", idn);
304 }
305 else {
306 fprintf(stderr, "Missing fields in .msgs request: %s, %s\n\n", id, pass);
307 }
308 break;
309 }
310
311 if (boundary) {
312 char *part = after_headers, *after_sub_headers, *header, *after_header;
313 size_t part_len;
314
315 part = strstr(part, boundary);
316 if (!part) {
317 fprintf(stderr, "Missing first multipart boundary\n");
318 break; 111 break;
319 } 112 }
320 part += boundary_len; 113
321 114 if (s1 + 4 + clen > back)
322 while (1) { 115 continue;
323 char *name = NULL, *filename = NULL, *type = NULL;
324
325 if (part[0] == '-' && part[1] == '-')
326 break;
327
328 if (*part != '\r') {
329 fprintf(stderr, "No \\r after multipart boundary\n");
330 goto done;
331 }
332 ++part;
333 if (*part != '\n') {
334 fprintf(stderr, "No \\n after multipart boundary\n");
335 goto done;
336 }
337 ++part;
338
339 if (!(after_sub_headers = strstr(part, "\r\n\r\n"))) {
340 fprintf(stderr, "Missing end of headers after multipart boundary\n");
341 goto done;
342 }
343 after_sub_headers[2] = 0;
344 after_sub_headers += 4;
345
346 for (header = part; after_header = strstr(header, "\r\n"); header = after_header + 2) {
347 char *colon, *after_colon;
348
349 *after_header = 0;
350 if (!(colon = strchr(header, ':'))) {
351 fprintf(stderr, "Missing colon in multipart sub-header\n");
352 goto done;
353 }
354 *colon++ = 0;
355 if (*colon++ != ' ') {
356 fprintf(stderr, "No space after colon in multipart sub-header\n");
357 goto done;
358 }
359
360 if (!strcasecmp(header, "Content-Disposition")) {
361 if (strncmp(colon, "form-data; ", 11)) {
362 fprintf(stderr, "Multipart data is not \"form-data\"\n");
363 goto done;
364 }
365
366 for (colon += 11; after_colon = strchr(colon, '='); colon = after_colon) {
367 char *data;
368 after_colon[0] = 0;
369 if (after_colon[1] != '"') {
370 fprintf(stderr, "Disposition setting is missing initial quote\n");
371 goto done;
372 }
373 data = after_colon+2;
374 if (!(after_colon = strchr(data, '"'))) {
375 fprintf(stderr, "Disposition setting is missing final quote\n");
376 goto done;
377 }
378 after_colon[0] = 0;
379 ++after_colon;
380 if (after_colon[0] == ';' && after_colon[1] == ' ')
381 after_colon += 2;
382
383 if (!strcasecmp(colon, "name"))
384 name = data;
385 else if (!strcasecmp(colon, "filename"))
386 filename = data;
387 }
388 } else if (!strcasecmp(header, "Content-Type")) {
389 type = colon;
390 }
391 }
392
393 part = memmem(after_sub_headers, back - after_sub_headers, boundary, boundary_len);
394 if (!part) {
395 fprintf(stderr, "Missing boundary after multipart payload\n");
396 goto done;
397 }
398 part[-2] = 0;
399 part_len = part - after_sub_headers - 2;
400 part[0] = 0;
401 part += boundary_len;
402
403 if (filename) {
404 uw_Basis_file f = {filename, type, {part_len, after_sub_headers}};
405
406 if (uw_set_file_input(ctx, name, f)) {
407 puts(uw_error_message(ctx));
408 goto done;
409 }
410 } else if (uw_set_input(ctx, name, after_sub_headers)) {
411 puts(uw_error_message(ctx));
412 goto done;
413 }
414 }
415 } 116 }
416 else { 117
417 if (is_post) 118 rr = uw_request(rc, ctx, buf, back - buf, sock);
418 inputs = after_headers;
419 else if (inputs = strchr(path, '?'))
420 *inputs++ = 0;
421
422 if (inputs) {
423 char *name, *value;
424
425 while (*inputs) {
426 name = inputs;
427 if (inputs = strchr(inputs, '&'))
428 *inputs++ = 0;
429 else
430 inputs = strchr(name, 0);
431
432 if (value = strchr(name, '=')) {
433 *value++ = 0;
434 if (uw_set_input(ctx, name, value)) {
435 puts(uw_error_message(ctx));
436 goto done;
437 }
438 }
439 else if (uw_set_input(ctx, name, "")) {
440 puts(uw_error_message(ctx));
441 goto done;
442 }
443 }
444 }
445 }
446
447 printf("Serving URI %s....\n", path);
448
449 while (1) {
450 size_t path_len = strlen(path);
451
452 uw_write_header(ctx, "HTTP/1.1 200 OK\r\n");
453
454 if (path_len + 1 > path_copy_size) {
455 path_copy_size = path_len + 1;
456 path_copy = realloc(path_copy, path_copy_size);
457 }
458 strcpy(path_copy, path);
459 fk = uw_begin(ctx, path_copy);
460 if (fk == SUCCESS || fk == RETURN_BLOB) {
461 uw_commit(ctx);
462 break;
463 } else if (fk == BOUNDED_RETRY) {
464 if (retries_left) {
465 printf("Error triggers bounded retry: %s\n", uw_error_message(ctx));
466 --retries_left;
467 }
468 else {
469 printf("Fatal error (out of retries): %s\n", uw_error_message(ctx));
470
471 try_rollback(ctx);
472
473 uw_reset_keep_error_message(ctx);
474 uw_write_header(ctx, "HTTP/1.1 500 Internal Server Error\n\r");
475 uw_write_header(ctx, "Content-type: text/plain\r\n");
476 uw_write(ctx, "Fatal error (out of retries): ");
477 uw_write(ctx, uw_error_message(ctx));
478 uw_write(ctx, "\n");
479
480 break;
481 }
482 } else if (fk == UNLIMITED_RETRY)
483 printf("Error triggers unlimited retry: %s\n", uw_error_message(ctx));
484 else if (fk == FATAL) {
485 printf("Fatal error: %s\n", uw_error_message(ctx));
486
487 try_rollback(ctx);
488
489 uw_reset_keep_error_message(ctx);
490 uw_write_header(ctx, "HTTP/1.1 500 Internal Server Error\r\n");
491 uw_write_header(ctx, "Content-type: text/html\r\n");
492 uw_write(ctx, "<html><head><title>Fatal Error</title></head><body>");
493 uw_write(ctx, "Fatal error: ");
494 uw_write(ctx, uw_error_message(ctx));
495 uw_write(ctx, "\n</body></html>");
496
497 break;
498 } else {
499 printf("Unknown uw_handle return code!\n");
500
501 try_rollback(ctx);
502
503 uw_reset_keep_request(ctx);
504 uw_write_header(ctx, "HTTP/1.1 500 Internal Server Error\n\r");
505 uw_write_header(ctx, "Content-type: text/plain\r\n");
506 uw_write(ctx, "Unknown uw_handle return code!\n");
507
508 break;
509 }
510
511 if (try_rollback(ctx))
512 break;
513
514 uw_reset_keep_request(ctx);
515 }
516
517 uw_send(ctx, sock); 119 uw_send(ctx, sock);
518 120
519 printf("Done with client.\n\n"); 121 if (rr == SERVED || rr == FAILED)
520 uw_memstats(ctx); 122 close(sock);
123 else if (rr != KEEP_OPEN)
124 fprintf(stderr, "Illegal uw_request return code: %d\n", rr);
125
521 break; 126 break;
522 } 127 }
523 } 128 }
524 129
525 done:
526 if (!dont_close)
527 close(sock);
528 uw_reset(ctx); 130 uw_reset(ctx);
529 }
530 }
531
532 static void *client_pruner(void *data) {
533 uw_context ctx = new_context();
534
535 if (!ctx)
536 exit(1);
537
538 while (1) {
539 uw_prune_clients(ctx);
540 sleep(5);
541 } 131 }
542 } 132 }
543 133
544 static void help(char *cmd) { 134 static void help(char *cmd) {
545 printf("Usage: %s [-p <port>] [-t <thread-count>]\n", cmd); 135 printf("Usage: %s [-p <port>] [-t <thread-count>]\n", cmd);
546 } 136 }
547 137
548 static void sigint(int signum) { 138 static void sigint(int signum) {
549 printf("Exiting....\n"); 139 printf("Exiting....\n");
550 exit(0); 140 exit(0);
551 }
552
553 static void initialize() {
554 uw_context ctx;
555 failure_kind fk;
556
557 init_crypto();
558
559 ctx = new_context();
560
561 if (!ctx)
562 exit(1);
563
564 for (fk = uw_initialize(ctx); fk == UNLIMITED_RETRY; fk = uw_initialize(ctx)) {
565 printf("Unlimited retry during init: %s\n", uw_error_message(ctx));
566 uw_db_rollback(ctx);
567 uw_reset(ctx);
568 }
569
570 if (fk != SUCCESS) {
571 printf("Failed to initialize database! %s\n", uw_error_message(ctx));
572 uw_db_rollback(ctx);
573 exit(1);
574 }
575
576 uw_free(ctx);
577 } 141 }
578 142
579 int main(int argc, char *argv[]) { 143 int main(int argc, char *argv[]) {
580 // The skeleton for this function comes from Beej's sockets tutorial. 144 // The skeleton for this function comes from Beej's sockets tutorial.
581 int sockfd; // listen on sock_fd 145 int sockfd; // listen on sock_fd
620 fprintf(stderr, "Unexpected getopt() behavior\n"); 184 fprintf(stderr, "Unexpected getopt() behavior\n");
621 return 1; 185 return 1;
622 } 186 }
623 } 187 }
624 188
625 uw_global_init(); 189 uw_request_init();
626 initialize();
627 190
628 names = calloc(nthreads, sizeof(int)); 191 names = calloc(nthreads, sizeof(int));
629 192
630 sockfd = socket(PF_INET, SOCK_STREAM, 0); // do some error checking! 193 sockfd = socket(PF_INET, SOCK_STREAM, 0); // do some error checking!
631 194