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;