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