Mercurial > urweb
comparison src/c/urweb.c @ 672:df6eb58de040
Fix some AJAX annoyances
author | Adam Chlipala <adamc@hcoop.net> |
---|---|
date | Tue, 24 Mar 2009 15:05:28 -0400 |
parents | 729e65db2e2f |
children | a8effb6159c2 |
comparison
equal
deleted
inserted
replaced
671:729e65db2e2f | 672:df6eb58de040 |
---|---|
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 static void buf_append(buf *b, const char *s, size_t len) { | 79 static void buf_append(buf *b, const char *s, size_t len) { |
80 buf_check(b, len); | 80 buf_check(b, len+1); |
81 memcpy(b->front, s, len); | 81 memcpy(b->front, s, len); |
82 b->front += len; | 82 b->front += len; |
83 } | 83 *b->front = 0; |
84 | 84 } |
85 | 85 |
86 // Persistent client state | 86 |
87 // Persistent state types | |
87 | 88 |
88 typedef enum { UNUSED, USED } usage; | 89 typedef enum { UNUSED, USED } usage; |
90 | |
91 typedef struct channel_list { | |
92 struct channel *data; | |
93 struct channel_list *next; | |
94 } channel_list; | |
89 | 95 |
90 typedef struct client { | 96 typedef struct client { |
91 size_t id; | 97 size_t id; |
92 usage mode; | 98 usage mode; |
93 union { | 99 union { |
97 int pass; | 103 int pass; |
98 buf msgs; | 104 buf msgs; |
99 int sock; | 105 int sock; |
100 time_t last_contact; | 106 time_t last_contact; |
101 unsigned refcount; | 107 unsigned refcount; |
108 channel_list *channels; | |
102 } used; | 109 } used; |
103 } data; | 110 } data; |
104 } client; | 111 } client; |
105 | |
106 static client **clients, *clients_free; | |
107 static size_t n_clients; | |
108 | |
109 static pthread_mutex_t clients_mutex = PTHREAD_MUTEX_INITIALIZER; | |
110 | |
111 static client *uw_new_client() { | |
112 client *c; | |
113 | |
114 pthread_mutex_lock(&clients_mutex); | |
115 | |
116 if (clients_free) { | |
117 c = clients_free; | |
118 clients_free = clients_free->data.next; | |
119 } | |
120 else { | |
121 ++n_clients; | |
122 clients = realloc(clients, sizeof(client) * n_clients); | |
123 c = malloc(sizeof(client)); | |
124 c->id = n_clients-1; | |
125 clients[n_clients-1] = c; | |
126 } | |
127 | |
128 c->mode = USED; | |
129 pthread_mutex_init(&c->data.used.lock, NULL); | |
130 c->data.used.pass = rand(); | |
131 c->data.used.sock = -1; | |
132 c->data.used.last_contact = time(NULL); | |
133 c->data.used.refcount = 0; | |
134 buf_init(&c->data.used.msgs, 0); | |
135 | |
136 pthread_mutex_unlock(&clients_mutex); | |
137 | |
138 return c; | |
139 } | |
140 | |
141 static const char begin_msgs[] = "HTTP/1.1 200 OK\r\nContent-type: text/plain\r\n\r\n"; | |
142 | |
143 static client *uw_find_client(size_t id) { | |
144 client *c; | |
145 | |
146 pthread_mutex_lock(&clients_mutex); | |
147 | |
148 if (id >= n_clients) { | |
149 pthread_mutex_unlock(&clients_mutex); | |
150 return NULL; | |
151 } | |
152 | |
153 c = clients[id]; | |
154 | |
155 if (c->mode != USED) { | |
156 pthread_mutex_unlock(&clients_mutex); | |
157 return NULL; | |
158 } | |
159 | |
160 pthread_mutex_lock(&c->data.used.lock); | |
161 ++c->data.used.refcount; | |
162 pthread_mutex_unlock(&c->data.used.lock); | |
163 pthread_mutex_unlock(&clients_mutex); | |
164 return c; | |
165 } | |
166 | |
167 static void uw_release_client(client *c) { | |
168 pthread_mutex_lock(&c->data.used.lock); | |
169 --c->data.used.refcount; | |
170 pthread_mutex_unlock(&c->data.used.lock); | |
171 } | |
172 | |
173 void uw_client_connect(size_t id, int pass, int sock) { | |
174 client *c = uw_find_client(id); | |
175 | |
176 if (c == NULL) { | |
177 close(sock); | |
178 fprintf(stderr, "Out-of-bounds client request (%d)\n", (int)id); | |
179 return; | |
180 } | |
181 | |
182 uw_release_client(c); | |
183 | |
184 pthread_mutex_lock(&c->data.used.lock); | |
185 | |
186 if (pass != c->data.used.pass) { | |
187 pthread_mutex_unlock(&c->data.used.lock); | |
188 close(sock); | |
189 fprintf(stderr, "Wrong client password (%d)\n", (int)id); | |
190 return; | |
191 } | |
192 | |
193 if (c->data.used.sock != -1) { | |
194 pthread_mutex_unlock(&c->data.used.lock); | |
195 close(sock); | |
196 fprintf(stderr, "Duplicate client connection (%d)\n", (int)id); | |
197 return; | |
198 } | |
199 | |
200 c->data.used.last_contact = time(NULL); | |
201 | |
202 if (buf_used(&c->data.used.msgs) > 0) { | |
203 uw_really_send(sock, begin_msgs, sizeof(begin_msgs) - 1); | |
204 uw_really_send(sock, c->data.used.msgs.start, buf_used(&c->data.used.msgs)); | |
205 close(sock); | |
206 } | |
207 else | |
208 c->data.used.sock = sock; | |
209 | |
210 pthread_mutex_unlock(&c->data.used.lock); | |
211 } | |
212 | |
213 static void uw_free_client(client *c) { | |
214 printf("Freeing client %d\n", c->id); | |
215 | |
216 if (c->mode == USED && c->data.used.sock != -1) | |
217 close(c->data.used.sock); | |
218 | |
219 pthread_mutex_destroy(&c->data.used.lock); | |
220 buf_free(&c->data.used.msgs); | |
221 c->mode = UNUSED; | |
222 c->data.next = clients_free; | |
223 clients_free = c; | |
224 } | |
225 | |
226 void uw_prune_clients(time_t timeout) { | |
227 size_t i; | |
228 time_t cutoff; | |
229 | |
230 cutoff = time(NULL) - timeout; | |
231 | |
232 pthread_mutex_lock(&clients_mutex); | |
233 | |
234 for (i = 0; i < n_clients; ++i) { | |
235 if (clients[i]->mode == USED && clients[i]->data.used.last_contact < cutoff | |
236 && clients[i]->data.used.refcount == 0) | |
237 uw_free_client(clients[i]); | |
238 } | |
239 | |
240 pthread_mutex_unlock(&clients_mutex); | |
241 } | |
242 | |
243 | |
244 // Persistent channel state | |
245 | 112 |
246 typedef struct client_list { | 113 typedef struct client_list { |
247 client *data; | 114 client *data; |
248 struct client_list *next; | 115 struct client_list *next; |
249 } client_list; | 116 } client_list; |
258 client_list *clients; | 125 client_list *clients; |
259 unsigned refcount; | 126 unsigned refcount; |
260 } used; | 127 } used; |
261 } data; | 128 } data; |
262 } channel; | 129 } channel; |
130 | |
131 | |
132 // Persistent client state | |
133 | |
134 static client **clients, *clients_free; | |
135 static size_t n_clients; | |
136 | |
137 static pthread_mutex_t clients_mutex = PTHREAD_MUTEX_INITIALIZER; | |
138 | |
139 static client *uw_new_client() { | |
140 client *c; | |
141 | |
142 pthread_mutex_lock(&clients_mutex); | |
143 | |
144 if (clients_free) { | |
145 c = clients_free; | |
146 clients_free = clients_free->data.next; | |
147 } | |
148 else { | |
149 ++n_clients; | |
150 clients = realloc(clients, sizeof(client) * n_clients); | |
151 c = malloc(sizeof(client)); | |
152 c->id = n_clients-1; | |
153 clients[n_clients-1] = c; | |
154 } | |
155 | |
156 c->mode = USED; | |
157 pthread_mutex_init(&c->data.used.lock, NULL); | |
158 c->data.used.pass = rand(); | |
159 c->data.used.sock = -1; | |
160 c->data.used.last_contact = time(NULL); | |
161 buf_init(&c->data.used.msgs, 0); | |
162 c->data.used.refcount = 0; | |
163 c->data.used.channels = NULL; | |
164 | |
165 pthread_mutex_unlock(&clients_mutex); | |
166 | |
167 return c; | |
168 } | |
169 | |
170 static const char begin_msgs[] = "HTTP/1.1 200 OK\r\nContent-type: text/plain\r\n\r\n"; | |
171 | |
172 static client *uw_find_client(size_t id) { | |
173 client *c; | |
174 | |
175 pthread_mutex_lock(&clients_mutex); | |
176 | |
177 if (id >= n_clients) { | |
178 pthread_mutex_unlock(&clients_mutex); | |
179 return NULL; | |
180 } | |
181 | |
182 c = clients[id]; | |
183 | |
184 if (c->mode != USED) { | |
185 pthread_mutex_unlock(&clients_mutex); | |
186 return NULL; | |
187 } | |
188 | |
189 pthread_mutex_lock(&c->data.used.lock); | |
190 ++c->data.used.refcount; | |
191 pthread_mutex_unlock(&c->data.used.lock); | |
192 pthread_mutex_unlock(&clients_mutex); | |
193 return c; | |
194 } | |
195 | |
196 static void uw_release_client(client *c) { | |
197 pthread_mutex_lock(&c->data.used.lock); | |
198 --c->data.used.refcount; | |
199 pthread_mutex_unlock(&c->data.used.lock); | |
200 } | |
201 | |
202 void uw_client_connect(size_t id, int pass, int sock) { | |
203 client *c = uw_find_client(id); | |
204 | |
205 if (c == NULL) { | |
206 close(sock); | |
207 fprintf(stderr, "Out-of-bounds client request (%d)\n", (int)id); | |
208 return; | |
209 } | |
210 | |
211 uw_release_client(c); | |
212 | |
213 pthread_mutex_lock(&c->data.used.lock); | |
214 | |
215 if (pass != c->data.used.pass) { | |
216 pthread_mutex_unlock(&c->data.used.lock); | |
217 close(sock); | |
218 fprintf(stderr, "Wrong client password (%d)\n", (int)id); | |
219 return; | |
220 } | |
221 | |
222 if (c->data.used.sock != -1) { | |
223 pthread_mutex_unlock(&c->data.used.lock); | |
224 close(sock); | |
225 fprintf(stderr, "Duplicate client connection (%d)\n", (int)id); | |
226 return; | |
227 } | |
228 | |
229 c->data.used.last_contact = time(NULL); | |
230 | |
231 if (buf_used(&c->data.used.msgs) > 0) { | |
232 uw_really_send(sock, begin_msgs, sizeof(begin_msgs) - 1); | |
233 uw_really_send(sock, c->data.used.msgs.start, buf_used(&c->data.used.msgs)); | |
234 close(sock); | |
235 } | |
236 else | |
237 c->data.used.sock = sock; | |
238 | |
239 pthread_mutex_unlock(&c->data.used.lock); | |
240 } | |
241 | |
242 | |
243 static void uw_free_client(client *c) { | |
244 channel_list *chs; | |
245 | |
246 printf("Freeing client %d\n", c->id); | |
247 | |
248 if (c->mode == USED) { | |
249 pthread_mutex_lock(&c->data.used.lock); | |
250 | |
251 for (chs = c->data.used.channels; chs; ) { | |
252 client_list *prev, *cs; | |
253 | |
254 channel *ch = chs->data; | |
255 channel_list *tmp = chs->next; | |
256 free(chs); | |
257 chs = tmp; | |
258 | |
259 pthread_mutex_lock(&ch->data.used.lock); | |
260 for (prev = NULL, cs = ch->data.used.clients; cs; ) { | |
261 if (cs->data == c) { | |
262 client_list *tmp = cs->next; | |
263 free(cs); | |
264 cs = tmp; | |
265 if (prev) | |
266 prev->next = cs; | |
267 else | |
268 ch->data.used.clients = cs; | |
269 } | |
270 else { | |
271 prev = cs; | |
272 cs = cs->next; | |
273 } | |
274 } | |
275 pthread_mutex_unlock(&ch->data.used.lock); | |
276 } | |
277 | |
278 if (c->data.used.sock != -1) | |
279 close(c->data.used.sock); | |
280 | |
281 pthread_mutex_unlock(&c->data.used.lock); | |
282 pthread_mutex_destroy(&c->data.used.lock); | |
283 buf_free(&c->data.used.msgs); | |
284 c->mode = UNUSED; | |
285 | |
286 c->data.next = clients_free; | |
287 clients_free = c; | |
288 } | |
289 } | |
290 | |
291 void uw_prune_clients(time_t timeout) { | |
292 size_t i; | |
293 time_t cutoff; | |
294 | |
295 cutoff = time(NULL) - timeout; | |
296 | |
297 pthread_mutex_lock(&clients_mutex); | |
298 | |
299 for (i = 0; i < n_clients; ++i) { | |
300 if (clients[i]->mode == USED && clients[i]->data.used.last_contact < cutoff | |
301 && clients[i]->data.used.refcount == 0) | |
302 uw_free_client(clients[i]); | |
303 } | |
304 | |
305 pthread_mutex_unlock(&clients_mutex); | |
306 } | |
307 | |
308 | |
309 // Persistent channel state | |
310 | |
263 | 311 |
264 static channel **channels, *channels_free; | 312 static channel **channels, *channels_free; |
265 static size_t n_channels; | 313 static size_t n_channels; |
266 | 314 |
267 static pthread_mutex_t channels_mutex = PTHREAD_MUTEX_INITIALIZER; | 315 static pthread_mutex_t channels_mutex = PTHREAD_MUTEX_INITIALIZER; |