Mercurial > urweb
comparison src/c/urweb.c @ 667:a93d5324f400
Dummy message delivery to clients
author | Adam Chlipala <adamc@hcoop.net> |
---|---|
date | Thu, 19 Mar 2009 16:34:13 -0400 |
parents | 5130228d2b29 |
children | b0c1a46b1f15 |
comparison
equal
deleted
inserted
replaced
666:5130228d2b29 | 667:a93d5324f400 |
---|---|
6 #include <strings.h> | 6 #include <strings.h> |
7 #include <ctype.h> | 7 #include <ctype.h> |
8 #include <setjmp.h> | 8 #include <setjmp.h> |
9 #include <stdarg.h> | 9 #include <stdarg.h> |
10 | 10 |
11 #include <pthread.h> | |
12 | |
11 #include "types.h" | 13 #include "types.h" |
12 | 14 |
13 uw_unit uw_unit_v = {}; | 15 uw_unit uw_unit_v = {}; |
16 | |
17 | |
18 // Socket extras | |
19 | |
20 int uw_really_send(int sock, const void *buf, size_t len) { | |
21 while (len > 0) { | |
22 size_t n = send(sock, buf, len, 0); | |
23 | |
24 if (n < 0) | |
25 return n; | |
26 | |
27 buf += n; | |
28 len -= n; | |
29 } | |
30 | |
31 return 0; | |
32 } | |
33 | |
34 | |
35 // Buffers | |
36 | |
37 typedef struct { | |
38 char *start, *front, *back; | |
39 } buf; | |
40 | |
41 static void buf_init(buf *b, size_t s) { | |
42 b->front = b->start = malloc(s); | |
43 b->back = b->front + s; | |
44 } | |
45 | |
46 static void buf_free(buf *b) { | |
47 free(b->start); | |
48 } | |
49 | |
50 static void buf_reset(buf *b) { | |
51 b->front = b->start; | |
52 } | |
53 | |
54 static void buf_check(buf *b, size_t extra) { | |
55 if (b->back - b->front < extra) { | |
56 size_t desired = b->front - b->start + extra, next; | |
57 char *new_heap; | |
58 | |
59 next = b->back - b->start; | |
60 if (next == 0) | |
61 next = 1; | |
62 for (; next < desired; next *= 2); | |
63 | |
64 new_heap = realloc(b->start, next); | |
65 b->front = new_heap + (b->front - b->start); | |
66 b->back = new_heap + next; | |
67 b->start = new_heap; | |
68 } | |
69 } | |
70 | |
71 static size_t buf_used(buf *b) { | |
72 return b->front - b->start; | |
73 } | |
74 | |
75 static size_t buf_avail(buf *b) { | |
76 return b->back - b->start; | |
77 } | |
78 | |
79 | |
80 // Cross-request state | |
81 | |
82 typedef enum { UNUSED, USED } usage; | |
83 | |
84 typedef struct client { | |
85 size_t id; | |
86 usage mode; | |
87 union { | |
88 struct client *next; | |
89 struct { | |
90 pthread_mutex_t lock; | |
91 int pass; | |
92 buf msgs; | |
93 int sock; | |
94 time_t last_contact; | |
95 } used; | |
96 } data; | |
97 } client; | |
98 | |
99 static client **clients, *clients_free; | |
100 static size_t n_clients; | |
101 | |
102 static pthread_mutex_t clients_mutex = PTHREAD_MUTEX_INITIALIZER; | |
103 | |
104 void uw_global_init() { | |
105 srand(time(NULL) ^ getpid()); | |
106 | |
107 clients = malloc(0); | |
108 } | |
109 | |
110 void uw_global_free() { | |
111 size_t i; | |
112 | |
113 for (i = 0; i < n_clients; ++i) | |
114 free(clients[i]); | |
115 | |
116 free(clients); | |
117 } | |
118 | |
119 static client *uw_new_client() { | |
120 client *c; | |
121 | |
122 pthread_mutex_lock(&clients_mutex); | |
123 | |
124 if (clients_free) { | |
125 c = clients_free; | |
126 clients_free = clients_free->data.next; | |
127 } | |
128 else { | |
129 ++n_clients; | |
130 clients = realloc(clients, sizeof(client) * n_clients); | |
131 c = malloc(sizeof(client)); | |
132 c->id = n_clients-1; | |
133 clients[n_clients-1] = c; | |
134 } | |
135 | |
136 c->mode = USED; | |
137 pthread_mutex_init(&c->data.used.lock, NULL); | |
138 c->data.used.pass = rand(); | |
139 c->data.used.sock = -1; | |
140 c->data.used.last_contact = time(NULL); | |
141 buf_init(&c->data.used.msgs, 0); | |
142 | |
143 pthread_mutex_unlock(&clients_mutex); | |
144 | |
145 return c; | |
146 } | |
147 | |
148 static const char begin_msgs[] = "HTTP/1.1 200 OK\r\nContent-type: text/plain\r\n\r\n"; | |
149 | |
150 void uw_client_connect(size_t id, int pass, int sock) { | |
151 client *c; | |
152 | |
153 pthread_mutex_lock(&clients_mutex); | |
154 | |
155 if (id >= n_clients) { | |
156 pthread_mutex_unlock(&clients_mutex); | |
157 close(sock); | |
158 fprintf(stderr, "Out-of-bounds client request (%d)\n", (int)id); | |
159 return; | |
160 } | |
161 | |
162 c = clients[id]; | |
163 | |
164 if (c->mode != USED) { | |
165 pthread_mutex_unlock(&clients_mutex); | |
166 close(sock); | |
167 fprintf(stderr, "Client request for unused ID (%d)\n", (int)id); | |
168 return; | |
169 } | |
170 | |
171 pthread_mutex_lock(&c->data.used.lock); | |
172 | |
173 if (pass != c->data.used.pass) { | |
174 pthread_mutex_unlock(&c->data.used.lock); | |
175 pthread_mutex_unlock(&clients_mutex); | |
176 close(sock); | |
177 fprintf(stderr, "Wrong client password (%d)\n", (int)id); | |
178 return; | |
179 } | |
180 | |
181 if (c->data.used.sock != -1) { | |
182 pthread_mutex_unlock(&c->data.used.lock); | |
183 pthread_mutex_unlock(&clients_mutex); | |
184 close(sock); | |
185 fprintf(stderr, "Duplicate client connection (%d)\n", (int)id); | |
186 return; | |
187 } | |
188 | |
189 c->data.used.last_contact = time(NULL); | |
190 | |
191 if (buf_used(&c->data.used.msgs) > 0) { | |
192 uw_really_send(sock, begin_msgs, sizeof(begin_msgs) - 1); | |
193 uw_really_send(sock, c->data.used.msgs.start, buf_used(&c->data.used.msgs)); | |
194 close(sock); | |
195 } | |
196 else { | |
197 uw_really_send(sock, begin_msgs, sizeof(begin_msgs) - 1); | |
198 uw_really_send(sock, "Hi!", 3); | |
199 close(sock); | |
200 //c->data.used.sock = sock; | |
201 } | |
202 | |
203 pthread_mutex_unlock(&c->data.used.lock); | |
204 pthread_mutex_unlock(&clients_mutex); | |
205 } | |
206 | |
207 static void uw_free_client(client *c) { | |
208 printf("Freeing client %d\n", c->id); | |
209 | |
210 if (c->mode == USED && c->data.used.sock != -1) | |
211 close(c->data.used.sock); | |
212 | |
213 pthread_mutex_destroy(&c->data.used.lock); | |
214 buf_free(&c->data.used.msgs); | |
215 c->mode = UNUSED; | |
216 c->data.next = clients_free; | |
217 clients_free = c; | |
218 } | |
219 | |
220 void uw_prune_clients(time_t timeout) { | |
221 size_t i; | |
222 time_t cutoff; | |
223 | |
224 cutoff = time(NULL) - timeout; | |
225 | |
226 pthread_mutex_lock(&clients_mutex); | |
227 | |
228 for (i = 0; i < n_clients; ++i) { | |
229 if (clients[i]->mode == USED && clients[i]->data.used.last_contact < cutoff) | |
230 uw_free_client(clients[i]); | |
231 } | |
232 | |
233 pthread_mutex_unlock(&clients_mutex); | |
234 } | |
235 | |
236 | |
237 // Single-request state | |
14 | 238 |
15 #define ERROR_BUF_LEN 1024 | 239 #define ERROR_BUF_LEN 1024 |
16 | 240 |
17 typedef struct regions { | 241 typedef struct regions { |
18 struct regions *next; | 242 struct regions *next; |
21 typedef struct { | 245 typedef struct { |
22 void (*func)(void*); | 246 void (*func)(void*); |
23 void *arg; | 247 void *arg; |
24 } cleanup; | 248 } cleanup; |
25 | 249 |
26 typedef struct { | |
27 char *start, *front, *back; | |
28 } buf; | |
29 | |
30 struct uw_context { | 250 struct uw_context { |
31 char *headers, *headers_end; | 251 char *headers, *headers_end; |
32 | 252 |
33 buf outHeaders, page, heap, script; | 253 buf outHeaders, page, heap, script; |
34 char **inputs; | 254 char **inputs; |
41 | 261 |
42 regions *regions; | 262 regions *regions; |
43 | 263 |
44 cleanup *cleanup, *cleanup_front, *cleanup_back; | 264 cleanup *cleanup, *cleanup_front, *cleanup_back; |
45 | 265 |
46 const char *script_header; | 266 const char *script_header, *url_prefix; |
47 | 267 |
48 char error_message[ERROR_BUF_LEN]; | 268 char error_message[ERROR_BUF_LEN]; |
49 }; | 269 }; |
50 | 270 |
51 extern int uw_inputs_len; | 271 extern int uw_inputs_len; |
52 | |
53 static void buf_init(buf *b, size_t s) { | |
54 b->front = b->start = malloc(s); | |
55 b->back = b->front + s; | |
56 } | |
57 | 272 |
58 uw_context uw_init(size_t outHeaders_len, size_t script_len, size_t page_len, size_t heap_len) { | 273 uw_context uw_init(size_t outHeaders_len, size_t script_len, size_t page_len, size_t heap_len) { |
59 uw_context ctx = malloc(sizeof(struct uw_context)); | 274 uw_context ctx = malloc(sizeof(struct uw_context)); |
60 | 275 |
61 ctx->headers = ctx->headers_end = NULL; | 276 ctx->headers = ctx->headers_end = NULL; |
62 | 277 |
63 buf_init(&ctx->outHeaders, outHeaders_len); | 278 buf_init(&ctx->outHeaders, outHeaders_len); |
64 buf_init(&ctx->page, page_len); | 279 buf_init(&ctx->page, page_len); |
65 buf_init(&ctx->heap, heap_len); | 280 buf_init(&ctx->heap, heap_len); |
66 buf_init(&ctx->script, script_len); | 281 buf_init(&ctx->script, script_len); |
282 ctx->script.start[0] = 0; | |
67 | 283 |
68 ctx->inputs = calloc(uw_inputs_len, sizeof(char *)); | 284 ctx->inputs = calloc(uw_inputs_len, sizeof(char *)); |
69 | 285 |
70 ctx->db = NULL; | 286 ctx->db = NULL; |
71 | 287 |
72 ctx->regions = NULL; | 288 ctx->regions = NULL; |
73 | 289 |
74 ctx->cleanup_front = ctx->cleanup_back = ctx->cleanup = malloc(0); | 290 ctx->cleanup_front = ctx->cleanup_back = ctx->cleanup = malloc(0); |
75 | 291 |
76 ctx->script_header = ""; | 292 ctx->script_header = ""; |
293 ctx->url_prefix = "/"; | |
77 | 294 |
78 ctx->error_message[0] = 0; | 295 ctx->error_message[0] = 0; |
79 | 296 |
80 ctx->source_count = 0; | 297 ctx->source_count = 0; |
81 | 298 |
86 ctx->db = db; | 303 ctx->db = db; |
87 } | 304 } |
88 | 305 |
89 void *uw_get_db(uw_context ctx) { | 306 void *uw_get_db(uw_context ctx) { |
90 return ctx->db; | 307 return ctx->db; |
91 } | |
92 | |
93 static void buf_free(buf *b) { | |
94 free(b->front); | |
95 } | 308 } |
96 | 309 |
97 void uw_free(uw_context ctx) { | 310 void uw_free(uw_context ctx) { |
98 buf_free(&ctx->outHeaders); | 311 buf_free(&ctx->outHeaders); |
99 buf_free(&ctx->script); | 312 buf_free(&ctx->script); |
102 free(ctx->inputs); | 315 free(ctx->inputs); |
103 free(ctx->cleanup); | 316 free(ctx->cleanup); |
104 free(ctx); | 317 free(ctx); |
105 } | 318 } |
106 | 319 |
107 static void buf_reset(buf *b) { | |
108 b->front = b->start; | |
109 } | |
110 | |
111 void uw_reset_keep_error_message(uw_context ctx) { | 320 void uw_reset_keep_error_message(uw_context ctx) { |
112 buf_reset(&ctx->outHeaders); | 321 buf_reset(&ctx->outHeaders); |
113 buf_reset(&ctx->script); | 322 buf_reset(&ctx->script); |
323 ctx->script.start[0] = 0; | |
114 buf_reset(&ctx->page); | 324 buf_reset(&ctx->page); |
115 buf_reset(&ctx->heap); | 325 buf_reset(&ctx->heap); |
116 ctx->regions = NULL; | 326 ctx->regions = NULL; |
117 ctx->cleanup_front = ctx->cleanup; | 327 ctx->cleanup_front = ctx->cleanup; |
118 ctx->source_count = 0; | 328 ctx->source_count = 0; |
247 | 457 |
248 void uw_set_script_header(uw_context ctx, const char *s) { | 458 void uw_set_script_header(uw_context ctx, const char *s) { |
249 ctx->script_header = s; | 459 ctx->script_header = s; |
250 } | 460 } |
251 | 461 |
252 static void buf_check(uw_context ctx, buf *b, size_t extra, const char *desc) { | 462 void uw_set_url_prefix(uw_context ctx, const char *s) { |
463 ctx->url_prefix = s; | |
464 } | |
465 | |
466 | |
467 static void buf_check_ctx(uw_context ctx, buf *b, size_t extra, const char *desc) { | |
253 if (b->back - b->front < extra) { | 468 if (b->back - b->front < extra) { |
254 size_t desired = b->front - b->start + extra, next; | 469 size_t desired = b->front - b->start + extra, next; |
255 char *new_heap; | 470 char *new_heap; |
256 | 471 |
257 next = b->back - b->start; | 472 next = b->back - b->start; |
261 | 476 |
262 new_heap = realloc(b->start, next); | 477 new_heap = realloc(b->start, next); |
263 b->front = new_heap + (b->front - b->start); | 478 b->front = new_heap + (b->front - b->start); |
264 b->back = new_heap + next; | 479 b->back = new_heap + next; |
265 | 480 |
266 if (desc && new_heap != b->start) { | 481 if (new_heap != b->start) { |
267 b->start = new_heap; | 482 b->start = new_heap; |
268 uw_error(ctx, UNLIMITED_RETRY, "Couldn't allocate new %s contiguously", desc); | 483 uw_error(ctx, UNLIMITED_RETRY, "Couldn't allocate new %s contiguously", desc); |
269 } | 484 } |
270 | 485 |
271 b->start = new_heap; | 486 b->start = new_heap; |
272 } | 487 } |
273 } | 488 } |
274 | 489 |
275 static void uw_check_heap(uw_context ctx, size_t extra) { | 490 static void uw_check_heap(uw_context ctx, size_t extra) { |
276 buf_check(ctx, &ctx->heap, extra, "heap chunk"); | 491 buf_check_ctx(ctx, &ctx->heap, extra, "heap chunk"); |
277 } | 492 } |
278 | 493 |
279 void *uw_malloc(uw_context ctx, size_t len) { | 494 void *uw_malloc(uw_context ctx, size_t len) { |
280 void *result; | 495 void *result; |
281 | 496 |
303 if (r == NULL) | 518 if (r == NULL) |
304 uw_error(ctx, FATAL, "Region stack underflow"); | 519 uw_error(ctx, FATAL, "Region stack underflow"); |
305 | 520 |
306 ctx->heap.front = (char *) r; | 521 ctx->heap.front = (char *) r; |
307 ctx->regions = r->next; | 522 ctx->regions = r->next; |
308 } | |
309 | |
310 static size_t buf_used(buf *b) { | |
311 return b->front - b->start; | |
312 } | |
313 | |
314 static size_t buf_avail(buf *b) { | |
315 return b->back - b->start; | |
316 } | 523 } |
317 | 524 |
318 void uw_memstats(uw_context ctx) { | 525 void uw_memstats(uw_context ctx) { |
319 printf("Headers: %d/%d\n", buf_used(&ctx->outHeaders), buf_avail(&ctx->outHeaders)); | 526 printf("Headers: %d/%d\n", buf_used(&ctx->outHeaders), buf_avail(&ctx->outHeaders)); |
320 printf("Script: %d/%d\n", buf_used(&ctx->script), buf_avail(&ctx->script)); | 527 printf("Script: %d/%d\n", buf_used(&ctx->script), buf_avail(&ctx->script)); |
321 printf("Page: %d/%d\n", buf_used(&ctx->page), buf_avail(&ctx->page)); | 528 printf("Page: %d/%d\n", buf_used(&ctx->page), buf_avail(&ctx->page)); |
322 printf("Heap: %d/%d\n", buf_used(&ctx->heap), buf_avail(&ctx->heap)); | 529 printf("Heap: %d/%d\n", buf_used(&ctx->heap), buf_avail(&ctx->heap)); |
323 } | 530 } |
324 | 531 |
325 int uw_really_send(int sock, const void *buf, size_t len) { | |
326 while (len > 0) { | |
327 size_t n = send(sock, buf, len, 0); | |
328 | |
329 if (n < 0) | |
330 return n; | |
331 | |
332 buf += n; | |
333 len -= n; | |
334 } | |
335 | |
336 return 0; | |
337 } | |
338 | |
339 int uw_send(uw_context ctx, int sock) { | 532 int uw_send(uw_context ctx, int sock) { |
340 int n = uw_really_send(sock, ctx->outHeaders.start, ctx->outHeaders.front - ctx->outHeaders.start); | 533 int n = uw_really_send(sock, ctx->outHeaders.start, ctx->outHeaders.front - ctx->outHeaders.start); |
341 | 534 |
342 if (n < 0) | 535 if (n < 0) |
343 return n; | 536 return n; |
349 | 542 |
350 return uw_really_send(sock, ctx->page.start, ctx->page.front - ctx->page.start); | 543 return uw_really_send(sock, ctx->page.start, ctx->page.front - ctx->page.start); |
351 } | 544 } |
352 | 545 |
353 static void uw_check_headers(uw_context ctx, size_t extra) { | 546 static void uw_check_headers(uw_context ctx, size_t extra) { |
354 buf_check(ctx, &ctx->outHeaders, extra, NULL); | 547 buf_check(&ctx->outHeaders, extra); |
355 } | 548 } |
356 | 549 |
357 void uw_write_header(uw_context ctx, uw_Basis_string s) { | 550 void uw_write_header(uw_context ctx, uw_Basis_string s) { |
358 int len = strlen(s); | 551 int len = strlen(s); |
359 | 552 |
361 strcpy(ctx->outHeaders.front, s); | 554 strcpy(ctx->outHeaders.front, s); |
362 ctx->outHeaders.front += len; | 555 ctx->outHeaders.front += len; |
363 } | 556 } |
364 | 557 |
365 static void uw_check_script(uw_context ctx, size_t extra) { | 558 static void uw_check_script(uw_context ctx, size_t extra) { |
366 buf_check(ctx, &ctx->script, extra, NULL); | 559 buf_check(&ctx->script, extra); |
367 } | 560 } |
368 | 561 |
369 void uw_write_script(uw_context ctx, uw_Basis_string s) { | 562 void uw_write_script(uw_context ctx, uw_Basis_string s) { |
370 int len = strlen(s); | 563 int len = strlen(s); |
371 | 564 |
373 strcpy(ctx->script.front, s); | 566 strcpy(ctx->script.front, s); |
374 ctx->script.front += len; | 567 ctx->script.front += len; |
375 } | 568 } |
376 | 569 |
377 const char *uw_Basis_get_script(uw_context ctx, uw_unit u) { | 570 const char *uw_Basis_get_script(uw_context ctx, uw_unit u) { |
378 if (ctx->script.front == ctx->script.start) { | 571 if (ctx->script_header[0] == 0) |
379 return ctx->script_header; | 572 return ""; |
380 } else { | 573 else { |
381 char *r = uw_malloc(ctx, 41 + (ctx->script.front - ctx->script.start) + strlen(ctx->script_header)); | 574 int pass; |
382 | 575 client *c = uw_new_client(&pass); |
383 sprintf(r, "%s<script>%s</script>", ctx->script_header, ctx->script.start); | 576 |
577 char *r = uw_malloc(ctx, strlen(ctx->script_header) + 56 + 2 * INTS_MAX + buf_used(&ctx->script) | |
578 + strlen(ctx->url_prefix)); | |
579 sprintf(r, "%s<script>client_id=%d;client_pass=%d;url_prefix=\"%s\";%s</script>", | |
580 ctx->script_header, (int)c->id, c->data.used.pass, ctx->url_prefix, ctx->script.start); | |
384 return r; | 581 return r; |
385 } | 582 } |
583 } | |
584 | |
585 const char *uw_Basis_get_listener(uw_context ctx, uw_unit u) { | |
586 if (ctx->script_header[0] == 0) | |
587 return ""; | |
588 else | |
589 return " onload='listener()'"; | |
386 } | 590 } |
387 | 591 |
388 uw_Basis_string uw_Basis_jsifyString(uw_context ctx, uw_Basis_string s) { | 592 uw_Basis_string uw_Basis_jsifyString(uw_context ctx, uw_Basis_string s) { |
389 char *r, *s2; | 593 char *r, *s2; |
390 | 594 |
484 | 688 |
485 return uw_unit_v; | 689 return uw_unit_v; |
486 } | 690 } |
487 | 691 |
488 static void uw_check(uw_context ctx, size_t extra) { | 692 static void uw_check(uw_context ctx, size_t extra) { |
489 buf_check(ctx, &ctx->page, extra, NULL); | 693 buf_check(&ctx->page, extra); |
490 } | 694 } |
491 | 695 |
492 static void uw_writec_unsafe(uw_context ctx, char c) { | 696 static void uw_writec_unsafe(uw_context ctx, char c) { |
493 *(ctx->page.front)++ = c; | 697 *(ctx->page.front)++ = c; |
494 } | 698 } |
1368 uw_write_header(ctx, prefix); | 1572 uw_write_header(ctx, prefix); |
1369 uw_write_header(ctx, "\r\n"); | 1573 uw_write_header(ctx, "\r\n"); |
1370 | 1574 |
1371 return uw_unit_v; | 1575 return uw_unit_v; |
1372 } | 1576 } |
1577 | |
1578 |