Mercurial > urweb
comparison src/c/urweb.c @ 668:b0c1a46b1f15
First message send delivered, but not interpreted
author | Adam Chlipala <adamc@hcoop.net> |
---|---|
date | Sun, 22 Mar 2009 15:05:07 -0400 |
parents | a93d5324f400 |
children | f68eee90dbcf |
comparison
equal
deleted
inserted
replaced
667:a93d5324f400 | 668:b0c1a46b1f15 |
---|---|
74 | 74 |
75 static size_t buf_avail(buf *b) { | 75 static size_t buf_avail(buf *b) { |
76 return b->back - b->start; | 76 return b->back - b->start; |
77 } | 77 } |
78 | 78 |
79 | 79 static void buf_append(buf *b, const char *s, size_t len) { |
80 // Cross-request state | 80 buf_check(b, len); |
81 memcpy(b->front, s, len); | |
82 b->front += len; | |
83 } | |
84 | |
85 | |
86 // Persistent client state | |
81 | 87 |
82 typedef enum { UNUSED, USED } usage; | 88 typedef enum { UNUSED, USED } usage; |
83 | 89 |
84 typedef struct client { | 90 typedef struct client { |
85 size_t id; | 91 size_t id; |
99 static client **clients, *clients_free; | 105 static client **clients, *clients_free; |
100 static size_t n_clients; | 106 static size_t n_clients; |
101 | 107 |
102 static pthread_mutex_t clients_mutex = PTHREAD_MUTEX_INITIALIZER; | 108 static pthread_mutex_t clients_mutex = PTHREAD_MUTEX_INITIALIZER; |
103 | 109 |
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() { | 110 static client *uw_new_client() { |
120 client *c; | 111 client *c; |
121 | 112 |
122 pthread_mutex_lock(&clients_mutex); | 113 pthread_mutex_lock(&clients_mutex); |
123 | 114 |
145 return c; | 136 return c; |
146 } | 137 } |
147 | 138 |
148 static const char begin_msgs[] = "HTTP/1.1 200 OK\r\nContent-type: text/plain\r\n\r\n"; | 139 static const char begin_msgs[] = "HTTP/1.1 200 OK\r\nContent-type: text/plain\r\n\r\n"; |
149 | 140 |
150 void uw_client_connect(size_t id, int pass, int sock) { | 141 static client *uw_find_client(size_t id) { |
151 client *c; | 142 client *c; |
152 | 143 |
153 pthread_mutex_lock(&clients_mutex); | 144 pthread_mutex_lock(&clients_mutex); |
154 | 145 |
155 if (id >= n_clients) { | 146 if (id >= n_clients) { |
156 pthread_mutex_unlock(&clients_mutex); | 147 pthread_mutex_unlock(&clients_mutex); |
148 return NULL; | |
149 } | |
150 | |
151 c = clients[id]; | |
152 | |
153 if (c->mode != USED) { | |
154 pthread_mutex_unlock(&clients_mutex); | |
155 return NULL; | |
156 } | |
157 | |
158 pthread_mutex_unlock(&clients_mutex); | |
159 return c; | |
160 } | |
161 | |
162 void uw_client_connect(size_t id, int pass, int sock) { | |
163 client *c = uw_find_client(id); | |
164 | |
165 if (c == NULL) { | |
157 close(sock); | 166 close(sock); |
158 fprintf(stderr, "Out-of-bounds client request (%d)\n", (int)id); | 167 fprintf(stderr, "Out-of-bounds client request (%d)\n", (int)id); |
159 return; | 168 return; |
160 } | 169 } |
161 | 170 |
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); | 171 pthread_mutex_lock(&c->data.used.lock); |
172 | 172 |
173 if (pass != c->data.used.pass) { | 173 if (pass != c->data.used.pass) { |
174 pthread_mutex_unlock(&c->data.used.lock); | 174 pthread_mutex_unlock(&c->data.used.lock); |
175 pthread_mutex_unlock(&clients_mutex); | |
176 close(sock); | 175 close(sock); |
177 fprintf(stderr, "Wrong client password (%d)\n", (int)id); | 176 fprintf(stderr, "Wrong client password (%d)\n", (int)id); |
178 return; | 177 return; |
179 } | 178 } |
180 | 179 |
181 if (c->data.used.sock != -1) { | 180 if (c->data.used.sock != -1) { |
182 pthread_mutex_unlock(&c->data.used.lock); | 181 pthread_mutex_unlock(&c->data.used.lock); |
183 pthread_mutex_unlock(&clients_mutex); | |
184 close(sock); | 182 close(sock); |
185 fprintf(stderr, "Duplicate client connection (%d)\n", (int)id); | 183 fprintf(stderr, "Duplicate client connection (%d)\n", (int)id); |
186 return; | 184 return; |
187 } | 185 } |
188 | 186 |
191 if (buf_used(&c->data.used.msgs) > 0) { | 189 if (buf_used(&c->data.used.msgs) > 0) { |
192 uw_really_send(sock, begin_msgs, sizeof(begin_msgs) - 1); | 190 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)); | 191 uw_really_send(sock, c->data.used.msgs.start, buf_used(&c->data.used.msgs)); |
194 close(sock); | 192 close(sock); |
195 } | 193 } |
196 else { | 194 else |
197 uw_really_send(sock, begin_msgs, sizeof(begin_msgs) - 1); | 195 c->data.used.sock = sock; |
198 uw_really_send(sock, "Hi!", 3); | |
199 close(sock); | |
200 //c->data.used.sock = sock; | |
201 } | |
202 | 196 |
203 pthread_mutex_unlock(&c->data.used.lock); | 197 pthread_mutex_unlock(&c->data.used.lock); |
204 pthread_mutex_unlock(&clients_mutex); | |
205 } | 198 } |
206 | 199 |
207 static void uw_free_client(client *c) { | 200 static void uw_free_client(client *c) { |
208 printf("Freeing client %d\n", c->id); | 201 printf("Freeing client %d\n", c->id); |
209 | 202 |
229 if (clients[i]->mode == USED && clients[i]->data.used.last_contact < cutoff) | 222 if (clients[i]->mode == USED && clients[i]->data.used.last_contact < cutoff) |
230 uw_free_client(clients[i]); | 223 uw_free_client(clients[i]); |
231 } | 224 } |
232 | 225 |
233 pthread_mutex_unlock(&clients_mutex); | 226 pthread_mutex_unlock(&clients_mutex); |
227 } | |
228 | |
229 | |
230 // Persistent channel state | |
231 | |
232 typedef struct client_list { | |
233 client *data; | |
234 struct client_list *next; | |
235 } client_list; | |
236 | |
237 typedef struct channel { | |
238 size_t id; | |
239 usage mode; | |
240 union { | |
241 struct channel *next; | |
242 struct { | |
243 pthread_mutex_t lock; | |
244 client_list *clients; | |
245 } used; | |
246 } data; | |
247 } channel; | |
248 | |
249 static channel **channels, *channels_free; | |
250 static size_t n_channels; | |
251 | |
252 static pthread_mutex_t channels_mutex = PTHREAD_MUTEX_INITIALIZER; | |
253 | |
254 static channel *uw_new_channel() { | |
255 channel *ch; | |
256 | |
257 pthread_mutex_lock(&channels_mutex); | |
258 | |
259 if (channels_free) { | |
260 ch = channels_free; | |
261 channels_free = channels_free->data.next; | |
262 } | |
263 else { | |
264 ++n_channels; | |
265 channels = realloc(channels, sizeof(channels) * n_channels); | |
266 ch = malloc(sizeof(channel)); | |
267 ch->id = n_channels-1; | |
268 channels[n_channels-1] = ch; | |
269 } | |
270 | |
271 ch->mode = USED; | |
272 pthread_mutex_init(&ch->data.used.lock, NULL); | |
273 ch->data.used.clients = NULL; | |
274 | |
275 pthread_mutex_unlock(&channels_mutex); | |
276 | |
277 return ch; | |
278 } | |
279 | |
280 static channel *uw_find_channel(size_t id) { | |
281 channel *ch = NULL; | |
282 | |
283 pthread_mutex_lock(&channels_mutex); | |
284 | |
285 if (id < n_channels && channels[id]->mode == USED) | |
286 ch = channels[id]; | |
287 | |
288 pthread_mutex_unlock(&channels_mutex); | |
289 | |
290 return ch; | |
291 } | |
292 | |
293 static void uw_subscribe(channel *ch, client *c) { | |
294 client_list *cs = malloc(sizeof(client_list)); | |
295 | |
296 pthread_mutex_lock(&ch->data.used.lock); | |
297 | |
298 cs->data = c; | |
299 cs->next = ch->data.used.clients; | |
300 ch->data.used.clients = cs; | |
301 | |
302 pthread_mutex_unlock(&ch->data.used.lock); | |
303 } | |
304 | |
305 static void uw_unsubscribe(channel *ch, client *c) { | |
306 client_list *prev, *cur, *tmp; | |
307 | |
308 pthread_mutex_lock(&ch->data.used.lock); | |
309 | |
310 for (prev = NULL, cur = ch->data.used.clients; cur; ) { | |
311 if (cur->data == c) { | |
312 if (prev) | |
313 prev->next = cur->next; | |
314 else | |
315 ch->data.used.clients = cur->next; | |
316 tmp = cur; | |
317 cur = cur->next; | |
318 free(tmp); | |
319 } | |
320 else { | |
321 prev = cur; | |
322 cur = cur->next; | |
323 } | |
324 } | |
325 | |
326 pthread_mutex_unlock(&ch->data.used.lock); | |
327 } | |
328 | |
329 static void uw_channel_send(channel *ch, const char *msg) { | |
330 size_t len = strlen(msg), preLen; | |
331 char pre[INTS_MAX + 2]; | |
332 client_list *cs; | |
333 | |
334 sprintf(pre, "%d\n", (int)ch->id); | |
335 preLen = strlen(pre); | |
336 | |
337 pthread_mutex_lock(&ch->data.used.lock); | |
338 | |
339 for (cs = ch->data.used.clients; cs; cs = cs->next) { | |
340 client *c = cs->data; | |
341 | |
342 pthread_mutex_lock(&c->data.used.lock); | |
343 | |
344 if (c->data.used.sock != -1) { | |
345 uw_really_send(c->data.used.sock, begin_msgs, sizeof(begin_msgs) - 1); | |
346 uw_really_send(c->data.used.sock, pre, preLen); | |
347 uw_really_send(c->data.used.sock, msg, len); | |
348 uw_really_send(c->data.used.sock, "\n", 1); | |
349 close(c->data.used.sock); | |
350 c->data.used.sock = -1; | |
351 } else { | |
352 buf_append(&c->data.used.msgs, pre, preLen); | |
353 buf_append(&c->data.used.msgs, msg, len); | |
354 buf_append(&c->data.used.msgs, "\n", 1); | |
355 } | |
356 | |
357 pthread_mutex_unlock(&c->data.used.lock); | |
358 } | |
359 | |
360 pthread_mutex_unlock(&ch->data.used.lock); | |
361 } | |
362 | |
363 | |
364 // Global entry points | |
365 | |
366 void uw_global_init() { | |
367 srand(time(NULL) ^ getpid()); | |
368 | |
369 clients = malloc(0); | |
370 channels = malloc(0); | |
234 } | 371 } |
235 | 372 |
236 | 373 |
237 // Single-request state | 374 // Single-request state |
238 | 375 |
580 ctx->script_header, (int)c->id, c->data.used.pass, ctx->url_prefix, ctx->script.start); | 717 ctx->script_header, (int)c->id, c->data.used.pass, ctx->url_prefix, ctx->script.start); |
581 return r; | 718 return r; |
582 } | 719 } |
583 } | 720 } |
584 | 721 |
585 const char *uw_Basis_get_listener(uw_context ctx, uw_unit u) { | 722 const char *uw_Basis_get_listener(uw_context ctx, uw_Basis_string onload) { |
586 if (ctx->script_header[0] == 0) | 723 if (ctx->script_header[0] == 0) |
587 return ""; | 724 return ""; |
588 else | 725 else if (onload[0] == 0) |
589 return " onload='listener()'"; | 726 return " onload='listener()'"; |
727 else { | |
728 uw_Basis_string s = uw_malloc(ctx, strlen(onload) + 22); | |
729 | |
730 sprintf(s, " onload='listener();%s'", onload); | |
731 return s; | |
732 } | |
590 } | 733 } |
591 | 734 |
592 uw_Basis_string uw_Basis_jsifyString(uw_context ctx, uw_Basis_string s) { | 735 uw_Basis_string uw_Basis_jsifyString(uw_context ctx, uw_Basis_string s) { |
593 char *r, *s2; | 736 char *r, *s2; |
594 | 737 |
939 r = atoll(*s); | 1082 r = atoll(*s); |
940 *s = new_s; | 1083 *s = new_s; |
941 return r; | 1084 return r; |
942 } | 1085 } |
943 | 1086 |
1087 uw_Basis_channel uw_Basis_unurlifyChannel(uw_context ctx, char **s) { | |
1088 return uw_Basis_unurlifyInt(ctx, s); | |
1089 } | |
1090 | |
944 uw_Basis_float uw_Basis_unurlifyFloat(uw_context ctx, char **s) { | 1091 uw_Basis_float uw_Basis_unurlifyFloat(uw_context ctx, char **s) { |
945 char *new_s = uw_unurlify_advance(*s); | 1092 char *new_s = uw_unurlify_advance(*s); |
946 uw_Basis_float r; | 1093 uw_Basis_float r; |
947 | 1094 |
948 r = atof(*s); | 1095 r = atof(*s); |
1028 uw_check(ctx, INTS_MAX); | 1175 uw_check(ctx, INTS_MAX); |
1029 sprintf(ctx->page.front, "%lld%n", n, &len); | 1176 sprintf(ctx->page.front, "%lld%n", n, &len); |
1030 ctx->page.front += len; | 1177 ctx->page.front += len; |
1031 | 1178 |
1032 return uw_unit_v; | 1179 return uw_unit_v; |
1180 } | |
1181 | |
1182 char *uw_Basis_htmlifyChannel(uw_context ctx, uw_Basis_channel ch) { | |
1183 return uw_Basis_htmlifyInt(ctx, ch); | |
1033 } | 1184 } |
1034 | 1185 |
1035 char *uw_Basis_htmlifyFloat(uw_context ctx, uw_Basis_float n) { | 1186 char *uw_Basis_htmlifyFloat(uw_context ctx, uw_Basis_float n) { |
1036 int len; | 1187 int len; |
1037 char *r; | 1188 char *r; |
1573 uw_write_header(ctx, "\r\n"); | 1724 uw_write_header(ctx, "\r\n"); |
1574 | 1725 |
1575 return uw_unit_v; | 1726 return uw_unit_v; |
1576 } | 1727 } |
1577 | 1728 |
1578 | 1729 uw_Basis_channel uw_Basis_new_channel(uw_context ctx, uw_unit u) { |
1730 return uw_new_channel()->id; | |
1731 } | |
1732 | |
1733 uw_unit uw_Basis_subscribe(uw_context ctx, uw_Basis_channel chn) { | |
1734 channel *ch = uw_find_channel(chn); | |
1735 | |
1736 if (ch == NULL) | |
1737 uw_error(ctx, FATAL, "Bad channel ID %d", (int)chn); | |
1738 else { | |
1739 size_t id = atoi(uw_Basis_requestHeader(ctx, "UrWeb-Client")); | |
1740 int pass = atoi(uw_Basis_requestHeader(ctx, "UrWeb-Pass")); | |
1741 client *c = uw_find_client(id); | |
1742 | |
1743 if (c == NULL) | |
1744 uw_error(ctx, FATAL, "Unknown client ID in subscription request"); | |
1745 else if (c->data.used.pass != pass) | |
1746 uw_error(ctx, FATAL, "Wrong client password in subscription request"); | |
1747 else | |
1748 uw_subscribe(ch, c); | |
1749 } | |
1750 } | |
1751 | |
1752 uw_unit uw_Basis_send(uw_context ctx, uw_Basis_channel chn, uw_Basis_string msg) { | |
1753 channel *ch = uw_find_channel(chn); | |
1754 | |
1755 if (ch == NULL) | |
1756 uw_error(ctx, FATAL, "Bad channel ID %d", (int)chn); | |
1757 else | |
1758 uw_channel_send(ch, msg); | |
1759 } |