agoo 2.5.1 → 2.5.2

Sign up to get free protection for your applications and to get access to all the features.

Potentially problematic release.


This version of agoo might be problematic. Click here for more details.

Files changed (56) hide show
  1. checksums.yaml +4 -4
  2. data/CHANGELOG.md +9 -1
  3. data/README.md +12 -1
  4. data/ext/agoo/agoo.c +4 -3
  5. data/ext/agoo/bind.c +51 -11
  6. data/ext/agoo/bind.h +9 -0
  7. data/ext/agoo/con.c +127 -89
  8. data/ext/agoo/con.h +24 -8
  9. data/ext/agoo/debug.c +2 -0
  10. data/ext/agoo/debug.h +1 -0
  11. data/ext/agoo/err.c +1 -1
  12. data/ext/agoo/err.h +2 -5
  13. data/ext/agoo/foo/agoo.c +109 -0
  14. data/ext/agoo/foo/con.c +1220 -0
  15. data/ext/agoo/foo/con.h +65 -0
  16. data/ext/agoo/foo/page.c +699 -0
  17. data/ext/agoo/foo/pub.c +131 -0
  18. data/ext/agoo/foo/pub.h +40 -0
  19. data/ext/agoo/foo/rserver.c +1016 -0
  20. data/ext/agoo/foo/server.c +303 -0
  21. data/ext/agoo/foo/server.h +67 -0
  22. data/ext/agoo/foo/upgraded.c +182 -0
  23. data/ext/agoo/hook.c +31 -0
  24. data/ext/agoo/hook.h +9 -10
  25. data/ext/agoo/{types.h → kinds.h} +3 -3
  26. data/ext/agoo/log.c +168 -83
  27. data/ext/agoo/log.h +6 -2
  28. data/ext/agoo/page.c +38 -32
  29. data/ext/agoo/pub.c +16 -0
  30. data/ext/agoo/pub.h +1 -0
  31. data/ext/agoo/queue.h +1 -0
  32. data/ext/agoo/req.c +95 -0
  33. data/ext/agoo/req.h +48 -0
  34. data/ext/agoo/request.c +10 -38
  35. data/ext/agoo/request.h +1 -34
  36. data/ext/agoo/res.h +1 -3
  37. data/ext/agoo/response.c +6 -248
  38. data/ext/agoo/response.h +2 -6
  39. data/ext/agoo/rlog.c +2 -1
  40. data/ext/agoo/rresponse.c +252 -0
  41. data/ext/agoo/rresponse.h +14 -0
  42. data/ext/agoo/rserver.c +43 -45
  43. data/ext/agoo/rserver.h +3 -24
  44. data/ext/agoo/rupgraded.c +303 -0
  45. data/ext/agoo/rupgraded.h +17 -0
  46. data/ext/agoo/server.c +125 -8
  47. data/ext/agoo/server.h +26 -4
  48. data/ext/agoo/sse.c +3 -1
  49. data/ext/agoo/upgraded.c +42 -280
  50. data/ext/agoo/upgraded.h +16 -8
  51. data/ext/agoo/websocket.c +13 -9
  52. data/lib/agoo/base.rb +84 -0
  53. data/lib/agoo/client.rb +25 -0
  54. data/lib/agoo/version.rb +1 -1
  55. data/test/bind_test.rb +2 -2
  56. metadata +22 -4
@@ -0,0 +1,65 @@
1
+ // Copyright (c) 2018, Peter Ohler, All rights reserved.
2
+
3
+ #ifndef __AGOO_CON_H__
4
+ #define __AGOO_CON_H__
5
+
6
+ #include <poll.h>
7
+ #include <pthread.h>
8
+ #include <stdbool.h>
9
+ #include <stdint.h>
10
+
11
+ #include "err.h"
12
+ #include "req.h"
13
+ #include "response.h"
14
+ #include "server.h"
15
+ #include "kinds.h"
16
+
17
+ #define MAX_HEADER_SIZE 8192
18
+
19
+ struct _Upgraded;
20
+ struct _Req;
21
+ struct _Res;
22
+ struct _Bind;
23
+ struct _Queue;
24
+
25
+ typedef struct _Con {
26
+ struct _Con *next;
27
+ int sock;
28
+ struct _Bind *bind;
29
+ struct pollfd *pp;
30
+ uint64_t id;
31
+ char buf[MAX_HEADER_SIZE];
32
+ size_t bcnt;
33
+
34
+ ssize_t mcnt; // how much has been read so far
35
+ ssize_t wcnt; // how much has been written
36
+
37
+ double timeout;
38
+ bool closing;
39
+ bool dead;
40
+ volatile bool hijacked;
41
+ struct _Req *req;
42
+ struct _Res *res_head;
43
+ struct _Res *res_tail;
44
+
45
+ struct _Upgraded *up; // only set for push connections
46
+ } *Con;
47
+
48
+ typedef struct _ConLoop {
49
+ struct _ConLoop *next;
50
+ struct _Queue pub_queue;
51
+ pthread_t thread;
52
+ int id;
53
+ } *ConLoop;
54
+
55
+ extern Con con_create(Err err, int sock, uint64_t id, struct _Bind *b);
56
+ extern void con_destroy(Con c);
57
+ extern const char* con_header_value(const char *header, int hlen, const char *key, int *vlen);
58
+
59
+ extern struct _ConLoop* conloop_create(Err err, int id);
60
+
61
+ extern bool con_http_read(Con c);
62
+ extern bool con_http_write(Con c);
63
+ extern short con_http_events(Con c);
64
+
65
+ #endif /* __AGOO_CON_H__ */
@@ -0,0 +1,699 @@
1
+ // Copyright 2016, 2018 by Peter Ohler, All Rights Reserved
2
+
3
+ #include <string.h>
4
+ #include <stdio.h>
5
+ #include <stdlib.h>
6
+ #include <sys/stat.h>
7
+ #include <unistd.h>
8
+
9
+ #include "debug.h"
10
+ #include "dtime.h"
11
+ #include "page.h"
12
+
13
+ #define PAGE_RECHECK_TIME 5.0
14
+
15
+ #define MAX_KEY_UNIQ 9
16
+ #define MAX_KEY_LEN 1024
17
+ #define PAGE_BUCKET_SIZE 1024
18
+ #define PAGE_BUCKET_MASK 1023
19
+
20
+ #define MAX_MIME_KEY_LEN 15
21
+ #define MIME_BUCKET_SIZE 64
22
+ #define MIME_BUCKET_MASK 63
23
+
24
+ typedef struct _Slot {
25
+ struct _Slot *next;
26
+ char key[MAX_KEY_LEN + 1];
27
+ Page value;
28
+ uint64_t hash;
29
+ int klen;
30
+ } *Slot;
31
+
32
+ typedef struct _MimeSlot {
33
+ struct _MimeSlot *next;
34
+ char key[MAX_MIME_KEY_LEN + 1];
35
+ char *value;
36
+ uint64_t hash;
37
+ int klen;
38
+ } *MimeSlot;
39
+
40
+ typedef struct _Cache {
41
+ Slot buckets[PAGE_BUCKET_SIZE];
42
+ MimeSlot muckets[MIME_BUCKET_SIZE];
43
+ char *root;
44
+ Group groups;
45
+ } *Cache;
46
+
47
+ typedef struct _Mime {
48
+ const char *suffix;
49
+ const char *type;
50
+ } *Mime;
51
+
52
+ // These are used for the initial load.
53
+ static struct _Mime mime_map[] = {
54
+ { "asc", "text/plain" },
55
+ { "avi", "video/x-msvideo" },
56
+ { "bin", "application/octet-stream" },
57
+ { "bmp", "image/bmp" },
58
+ { "cer", "application/pkix-cert" },
59
+ { "crl", "application/pkix-crl" },
60
+ { "crt", "application/x-x509-ca-cert" },
61
+ { "css", "text/css" },
62
+ { "doc", "application/msword" },
63
+ { "eot", "application/vnd.ms-fontobject" },
64
+ { "eps", "application/postscript" },
65
+ { "es5", "application/javascript" },
66
+ { "es6", "application/javascript" },
67
+ { "gif", "image/gif" },
68
+ { "htm", "text/html" },
69
+ { "html", "text/html" },
70
+ { "ico", "image/x-icon" },
71
+ { "jpeg", "image/jpeg" },
72
+ { "jpg", "image/jpeg" },
73
+ { "js", "application/javascript" },
74
+ { "json", "application/json" },
75
+ { "mov", "video/quicktime" },
76
+ { "mpe", "video/mpeg" },
77
+ { "mpeg", "video/mpeg" },
78
+ { "mpg", "video/mpeg" },
79
+ { "pdf", "application/pdf" },
80
+ { "png", "image/png" },
81
+ { "ppt", "application/vnd.ms-powerpoint" },
82
+ { "ps", "application/postscript" },
83
+ { "qt", "video/quicktime" },
84
+ { "rb", "text/plain" },
85
+ { "rtf", "application/rtf" },
86
+ { "sse", "text/plain" },
87
+ { "svg", "image/svg+xml" },
88
+ { "tif", "image/tiff" },
89
+ { "tiff", "image/tiff" },
90
+ { "ttf", "application/font-sfnt" },
91
+ { "txt", "text/plain" },
92
+ { "woff", "application/font-woff" },
93
+ { "woff2", "font/woff2" },
94
+ { "xls", "application/vnd.ms-excel" },
95
+ { "xml", "application/xml" },
96
+ { "zip", "application/zip" },
97
+ { NULL, NULL }
98
+ };
99
+
100
+ static const char page_fmt[] = "HTTP/1.1 200 OK\r\nContent-Type: %s\r\nContent-Length: %ld\r\n\r\n";
101
+
102
+ static struct _Cache cache = {
103
+ .buckets = {0},
104
+ .muckets = {0},
105
+ .root = NULL,
106
+ .groups = NULL,
107
+ };
108
+
109
+ static uint64_t
110
+ calc_hash(const char *key, int *lenp) {
111
+ int len = 0;
112
+ int klen = *lenp;
113
+ uint64_t h = 0;
114
+ bool special = false;
115
+ const uint8_t *k = (const uint8_t*)key;
116
+
117
+ for (; len < klen; k++) {
118
+ // narrow to most used range of 0x4D (77) in size
119
+ if (*k < 0x2D || 0x7A < *k) {
120
+ special = true;
121
+ }
122
+ // fast, just spread it out
123
+ h = 77 * h + (*k - 0x2D);
124
+ len++;
125
+ }
126
+
127
+ if (special) {
128
+ *lenp = -len;
129
+ } else {
130
+ *lenp = len;
131
+ }
132
+ return h;
133
+ }
134
+
135
+ // Buckets are a twist on the hash to mix it up a bit. Odd shifts and XORs.
136
+ static Slot*
137
+ get_bucketp(uint64_t h) {
138
+ return cache.buckets + (PAGE_BUCKET_MASK & (h ^ (h << 5) ^ (h >> 7)));
139
+ }
140
+
141
+ static MimeSlot*
142
+ get_mime_bucketp(uint64_t h) {
143
+ return cache.muckets + (MIME_BUCKET_MASK & (h ^ (h << 5) ^ (h >> 7)));
144
+ }
145
+
146
+ const char*
147
+ mime_get(const char *key) {
148
+ int klen = (int)strlen(key);
149
+ int len = klen;
150
+ int64_t h = calc_hash(key, &len);
151
+ MimeSlot *bucket = get_mime_bucketp(h);
152
+ MimeSlot s;
153
+ const char *v = NULL;
154
+
155
+ for (s = *bucket; NULL != s; s = s->next) {
156
+ if (h == (int64_t)s->hash && len == (int)s->klen &&
157
+ ((0 <= len && len <= MAX_KEY_UNIQ) || 0 == strncmp(s->key, key, klen))) {
158
+ v = s->value;
159
+ break;
160
+ }
161
+ }
162
+ return v;
163
+ }
164
+
165
+ Page
166
+ cache_get(const char *key, int klen) {
167
+ int len = klen;
168
+ int64_t h = calc_hash(key, &len);
169
+ Slot *bucket = get_bucketp(h);
170
+ Slot s;
171
+ Page v = NULL;
172
+
173
+ for (s = *bucket; NULL != s; s = s->next) {
174
+ if (h == (int64_t)s->hash && len == (int)s->klen &&
175
+ ((0 <= len && len <= MAX_KEY_UNIQ) || 0 == strncmp(s->key, key, klen))) {
176
+ v = s->value;
177
+ break;
178
+ }
179
+ }
180
+ return v;
181
+ }
182
+
183
+ int
184
+ mime_set(Err err, const char *key, const char *value) {
185
+ int klen = (int)strlen(key);
186
+ int len = klen;
187
+ int64_t h = calc_hash(key, &len);
188
+ MimeSlot *bucket = get_mime_bucketp(h);
189
+ MimeSlot s;
190
+
191
+ if (MAX_MIME_KEY_LEN < len) {
192
+ return err_set(err, ERR_ARG, "%s is too long for a file extension. Maximum is %d", key, MAX_MIME_KEY_LEN);
193
+ }
194
+ for (s = *bucket; NULL != s; s = s->next) {
195
+ if (h == (int64_t)s->hash && len == s->klen &&
196
+ ((0 <= len && len <= MAX_KEY_UNIQ) || 0 == strncmp(s->key, key, len))) {
197
+
198
+ DEBUG_FREE(mem_mime_slot, s->value);
199
+ free(s->value);
200
+ s->value = strdup(value);
201
+ return ERR_OK;
202
+ }
203
+ }
204
+ if (NULL == (s = (MimeSlot)malloc(sizeof(struct _MimeSlot)))) {
205
+ return err_set(err, ERR_ARG, "out of memory adding %s", key);
206
+ }
207
+ DEBUG_ALLOC(mem_mime_slot, s);
208
+ s->hash = h;
209
+ s->klen = len;
210
+ if (NULL == key) {
211
+ *s->key = '\0';
212
+ } else {
213
+ strcpy(s->key, key);
214
+ }
215
+ s->value = strdup(value);
216
+ s->next = *bucket;
217
+ *bucket = s;
218
+
219
+ return ERR_OK;
220
+ }
221
+
222
+ static Page
223
+ cache_set(const char *key, int klen, Page value) {
224
+ int len = klen;
225
+ int64_t h = calc_hash(key, &len);
226
+ Slot *bucket = get_bucketp(h);
227
+ Slot s;
228
+ Page old = NULL;
229
+
230
+ if (MAX_KEY_LEN < len) {
231
+ return value;
232
+ }
233
+ for (s = *bucket; NULL != s; s = s->next) {
234
+ if (h == (int64_t)s->hash && len == s->klen &&
235
+ ((0 <= len && len <= MAX_KEY_UNIQ) || 0 == strncmp(s->key, key, len))) {
236
+
237
+ old = s->value;
238
+ // replace
239
+ s->value = value;
240
+
241
+ return old;
242
+ }
243
+ }
244
+ if (NULL == (s = (Slot)malloc(sizeof(struct _Slot)))) {
245
+ return value;
246
+ }
247
+ DEBUG_ALLOC(mem_page_slot, s)
248
+ s->hash = h;
249
+ s->klen = len;
250
+ if (NULL == key) {
251
+ *s->key = '\0';
252
+ } else {
253
+ strncpy(s->key, key, len);
254
+ s->key[len] = '\0';
255
+ }
256
+ s->value = value;
257
+ s->next = *bucket;
258
+ *bucket = s;
259
+
260
+ return old;
261
+ }
262
+
263
+ void
264
+ pages_init() {
265
+ Mime m;
266
+ struct _Err err = ERR_INIT;
267
+
268
+ memset(&cache, 0, sizeof(struct _Cache));
269
+ cache.root = strdup(".");
270
+ for (m = mime_map; NULL != m->suffix; m++) {
271
+ mime_set(&err, m->suffix, m->type);
272
+ }
273
+ }
274
+
275
+ void
276
+ pages_set_root(const char *root) {
277
+ free(cache.root);
278
+ if (NULL == root) {
279
+ cache.root = NULL;
280
+ } else {
281
+ cache.root = strdup(root);
282
+ }
283
+ }
284
+
285
+ static void
286
+ page_destroy(Page p) {
287
+ if (NULL != p->resp) {
288
+ text_release(p->resp);
289
+ p->resp = NULL;
290
+ }
291
+ DEBUG_FREE(mem_page_path, p->path);
292
+ DEBUG_FREE(mem_page, p);
293
+ free(p->path);
294
+ free(p);
295
+ }
296
+
297
+ void
298
+ pages_cleanup() {
299
+ Slot *sp = cache.buckets;
300
+ Slot s;
301
+ Slot n;
302
+ MimeSlot *mp = cache.muckets;
303
+ MimeSlot sm;
304
+ MimeSlot m;
305
+ int i;
306
+
307
+ for (i = PAGE_BUCKET_SIZE; 0 < i; i--, sp++) {
308
+ for (s = *sp; NULL != s; s = n) {
309
+ n = s->next;
310
+ DEBUG_FREE(mem_page_slot, s);
311
+ page_destroy(s->value);
312
+ free(s);
313
+ }
314
+ *sp = NULL;
315
+ }
316
+ for (i = MIME_BUCKET_SIZE; 0 < i; i--, mp++) {
317
+ for (sm = *mp; NULL != sm; sm = m) {
318
+ m = sm->next;
319
+ DEBUG_FREE(mem_mime_slot, sm);
320
+ free(sm);
321
+ }
322
+ *mp = NULL;
323
+ }
324
+ free(cache.root);
325
+ }
326
+
327
+ static const char*
328
+ path_mime(const char *path) {
329
+ const char *suffix = path + strlen(path) - 1;
330
+ const char *mime = NULL;
331
+
332
+ for (; '.' != *suffix; suffix--) {
333
+ if (suffix <= path) {
334
+ suffix = NULL;
335
+ break;
336
+ }
337
+ }
338
+ if (suffix <= path) {
339
+ suffix = NULL;
340
+ }
341
+ if (NULL != suffix) {
342
+ suffix++;
343
+ mime = mime_get(suffix);
344
+ }
345
+ return mime;
346
+ }
347
+
348
+ // The page resp points to the page resp msg to save memory and reduce
349
+ // allocations.
350
+ Page
351
+ page_create(const char *path) {
352
+ Page p = (Page)malloc(sizeof(struct _Page));
353
+
354
+ if (NULL != p) {
355
+ DEBUG_ALLOC(mem_page, p);
356
+ p->resp = NULL;
357
+ if (NULL == path) {
358
+ p->path = NULL;
359
+ } else {
360
+ p->path = strdup(path);
361
+ DEBUG_ALLOC(mem_page_path, p->path);
362
+ }
363
+ p->mtime = 0;
364
+ p->last_check = 0.0;
365
+ p->immutable = false;
366
+ }
367
+ return p;
368
+ }
369
+
370
+ Page
371
+ page_immutable(Err err, const char *path, const char *content, int clen) {
372
+ Page p = (Page)malloc(sizeof(struct _Page));
373
+ const char *mime = path_mime(path);
374
+ long msize;
375
+ int cnt;
376
+ int plen = 0;
377
+
378
+ if (NULL == p) {
379
+ err_set(err, ERR_MEMORY, "Failed to allocate memory for page.");
380
+ return NULL;
381
+ }
382
+ DEBUG_ALLOC(mem_page, p);
383
+ if (NULL == path) {
384
+ p->path = NULL;
385
+ } else {
386
+ p->path = strdup(path);
387
+ plen = (int)strlen(path);
388
+ DEBUG_ALLOC(mem_page_path, p->path);
389
+ }
390
+ p->mtime = 0;
391
+ p->last_check = 0.0;
392
+ p->immutable = true;
393
+
394
+ if (NULL == mime) {
395
+ mime = "text/html";
396
+ }
397
+ if (0 == clen) {
398
+ clen = (int)strlen(content);
399
+ }
400
+ // Format size plus space for the length, the mime type, and some
401
+ // padding. Then add the content length.
402
+ msize = sizeof(page_fmt) + 60 + clen;
403
+ if (NULL == (p->resp = text_allocate((int)msize))) {
404
+ DEBUG_FREE(mem_page, p);
405
+ free(p);
406
+ err_set(err, ERR_MEMORY, "Failed to allocate memory for page content.");
407
+ return NULL;
408
+ }
409
+ cnt = sprintf(p->resp->text, page_fmt, mime, (long)clen);
410
+ msize = cnt + clen;
411
+ memcpy(p->resp->text + cnt, content, clen);
412
+ p->resp->text[msize] = '\0';
413
+ p->resp->len = msize;
414
+ text_ref(p->resp);
415
+
416
+ cache_set(path, plen, p);
417
+
418
+ return p;
419
+ }
420
+
421
+ static bool
422
+ update_contents(Page p) {
423
+ const char *mime = path_mime(p->path);
424
+ int plen = (int)strlen(p->path);
425
+ FILE *f;
426
+ long size;
427
+ struct stat fattr;
428
+ long msize;
429
+ int cnt;
430
+ struct stat fs;
431
+ Text t;
432
+
433
+ f = fopen(p->path, "rb");
434
+ // On linux a directory is opened by fopen (sometimes? all the time?) so
435
+ // fstat is called to get the file mode and verify it is a regular file or
436
+ // a symlink.
437
+ if (NULL != f) {
438
+ fstat(fileno(f), &fs);
439
+ if (!S_ISREG(fs.st_mode) && !S_ISLNK(fs.st_mode)) {
440
+ fclose(f);
441
+ f = NULL;
442
+ }
443
+ }
444
+ if (NULL == f) {
445
+ // If not found how about with a /index.html added?
446
+ if (NULL == mime) {
447
+ char path[1024];
448
+ int cnt;
449
+
450
+ if ('/' == p->path[plen - 1]) {
451
+ cnt = snprintf(path, sizeof(path), "%sindex.html", p->path);
452
+ } else {
453
+ cnt = snprintf(path, sizeof(path), "%s/index.html", p->path);
454
+ }
455
+ if ((int)sizeof(path) < cnt) {
456
+ return false;
457
+ }
458
+ if (NULL == (f = fopen(path, "rb"))) {
459
+ return false;
460
+ }
461
+ mime = "text/html";
462
+ } else {
463
+ return false;
464
+ }
465
+ }
466
+ if (NULL == mime) {
467
+ mime = "text/html";
468
+ }
469
+ if (0 != fseek(f, 0, SEEK_END)) {
470
+ fclose(f);
471
+ return false;
472
+ }
473
+ if (0 > (size = ftell(f))) {
474
+ fclose(f);
475
+ return false;
476
+ }
477
+ rewind(f);
478
+
479
+ // Format size plus space for the length, the mime type, and some
480
+ // padding. Then add the content length.
481
+ msize = sizeof(page_fmt) + 60 + size;
482
+ if (NULL == (t = text_allocate((int)msize))) {
483
+ return false;
484
+ }
485
+ cnt = sprintf(t->text, page_fmt, mime, size);
486
+ msize = cnt + size;
487
+ if (size != (long)fread(t->text + cnt, 1, size, f)) {
488
+ fclose(f);
489
+ text_release(t);
490
+ return false;
491
+ }
492
+ fclose(f);
493
+ t->text[msize] = '\0';
494
+ t->len = msize;
495
+ if (0 == stat(p->path, &fattr)) {
496
+ p->mtime = fattr.st_mtime;
497
+ } else {
498
+ p->mtime = 0;
499
+ }
500
+ if (NULL != p->resp) {
501
+ text_release(p->resp);
502
+ p->resp = NULL;
503
+ }
504
+ p->resp = t;
505
+ text_ref(p->resp);
506
+ p->last_check = dtime();
507
+
508
+ return true;
509
+ }
510
+
511
+ static void
512
+ page_remove(Page p) {
513
+ int len = (int)strlen(p->path);
514
+ int64_t h = calc_hash(p->path, &len);
515
+ Slot *bucket = get_bucketp(h);
516
+ Slot s;
517
+ Slot prev = NULL;
518
+
519
+ for (s = *bucket; NULL != s; s = s->next) {
520
+ if (h == (int64_t)s->hash && len == (int)s->klen &&
521
+ ((0 <= len && len <= MAX_KEY_UNIQ) || 0 == strncmp(s->key, p->path, len))) {
522
+
523
+ if (NULL == prev) {
524
+ *bucket = s->next;
525
+ } else {
526
+ prev->next = s->next;
527
+ }
528
+ DEBUG_FREE(mem_page_slot, s);
529
+ page_destroy(s->value);
530
+ free(s);
531
+
532
+ break;
533
+ }
534
+ prev = s;
535
+ }
536
+ }
537
+
538
+ static Page
539
+ page_check(Err err, Page page) {
540
+ if (!page->immutable) {
541
+ double now = dtime();
542
+
543
+ if (page->last_check + PAGE_RECHECK_TIME < now) {
544
+ struct stat fattr;
545
+
546
+ if (0 == stat(page->path, &fattr) && page->mtime != fattr.st_mtime) {
547
+ update_contents(page);
548
+ if (NULL == page->resp) {
549
+ page_remove(page);
550
+ err_set(err, ERR_NOT_FOUND, "not found.");
551
+ return NULL;
552
+ }
553
+ }
554
+ page->last_check = now;
555
+ }
556
+ }
557
+ return page;
558
+ }
559
+
560
+ Page
561
+ page_get(Err err, const char *path, int plen) {
562
+ Page page;
563
+
564
+ if (NULL != strstr(path, "../")) {
565
+ return NULL;
566
+ }
567
+ if (NULL == (page = cache_get(path, plen))) {
568
+ if (NULL != cache.root) {
569
+ Page old;
570
+ char full_path[2048];
571
+ char *s = stpcpy(full_path, cache.root);
572
+
573
+ if ('/' != *cache.root && '/' != *path) {
574
+ *s++ = '/';
575
+ }
576
+ if ((int)sizeof(full_path) <= plen + (s - full_path)) {
577
+ err_set(err, ERR_MEMORY, "Failed to allocate memory for page path.");
578
+ return NULL;
579
+ }
580
+ strncpy(s, path, plen);
581
+ s[plen] = '\0';
582
+ if (NULL == (page = page_create(full_path))) {
583
+ err_set(err, ERR_MEMORY, "Failed to allocate memory for Page.");
584
+ return NULL;
585
+ }
586
+ if (!update_contents(page) || NULL == page->resp) {
587
+ page_destroy(page);
588
+ err_set(err, ERR_NOT_FOUND, "not found.");
589
+ return NULL;
590
+ }
591
+ if (NULL != (old = cache_set(path, plen, page))) {
592
+ page_destroy(old);
593
+ }
594
+ }
595
+ } else {
596
+ page = page_check(err, page);
597
+ }
598
+ return page;
599
+ }
600
+
601
+ Page
602
+ group_get(Err err, const char *path, int plen) {
603
+ Page page = NULL;
604
+ Group g = NULL;
605
+ char full_path[2048];
606
+ char *s = NULL;
607
+ Dir d;
608
+
609
+ if (NULL != strstr(path, "../")) {
610
+ return NULL;
611
+ }
612
+ for (g = cache.groups; NULL != g; g = g->next) {
613
+ if (g->plen < plen && 0 == strncmp(path, g->path, g->plen) && '/' == path[g->plen]) {
614
+ break;
615
+ }
616
+ }
617
+ if (NULL == g) {
618
+ return NULL;
619
+ }
620
+ for (d = g->dirs; NULL != d; d = d->next) {
621
+ if ((int)sizeof(full_path) <= d->plen + plen) {
622
+ continue;
623
+ }
624
+ s = stpcpy(full_path, d->path);
625
+ strncpy(s, path + g->plen, plen - g->plen);
626
+ s += plen - g->plen;
627
+ *s = '\0';
628
+ if (NULL != (page = cache_get(full_path, (int)(s - full_path)))) {
629
+ break;
630
+ }
631
+ }
632
+ if (NULL == page) {
633
+ for (d = g->dirs; NULL != d; d = d->next) {
634
+ if ((int)sizeof(full_path) <= d->plen + plen) {
635
+ continue;
636
+ }
637
+ s = stpcpy(full_path, d->path);
638
+ strncpy(s, path + g->plen, plen - g->plen);
639
+ s += plen - g->plen;
640
+ *s = '\0';
641
+ if (0 == access(full_path, R_OK)) {
642
+ break;
643
+ }
644
+ }
645
+ if (NULL == d) {
646
+ return NULL;
647
+ }
648
+ plen = (int)(s - full_path);
649
+ path = full_path;
650
+ if (NULL == (page = cache_get(path, plen))) {
651
+ Page old;
652
+
653
+ if (NULL == (page = page_create(path))) {
654
+ err_set(err, ERR_MEMORY, "Failed to allocate memory for Page.");
655
+ return NULL;
656
+ }
657
+ if (!update_contents(page) || NULL == page->resp) {
658
+ page_destroy(page);
659
+ err_set(err, ERR_NOT_FOUND, "not found.");
660
+ return NULL;
661
+ }
662
+ if (NULL != (old = cache_set(path, plen, page))) {
663
+ page_destroy(old);
664
+ }
665
+ }
666
+ return page;
667
+ }
668
+ return page_check(err, page);
669
+ }
670
+
671
+ Group
672
+ group_create(const char *path) {
673
+ Group g = (Group)malloc(sizeof(struct _Group));
674
+
675
+ if (NULL != g) {
676
+ DEBUG_ALLOC(mem_group, g);
677
+ g->next = cache.groups;
678
+ cache.groups = g;
679
+ g->path = strdup(path);
680
+ g->plen = (int)strlen(path);
681
+ DEBUG_ALLOC(mem_group_path, g->path);
682
+ g->dirs = NULL;
683
+ }
684
+ return g;
685
+ }
686
+
687
+ void
688
+ group_add(Group g, const char *dir) {
689
+ Dir d = (Dir)malloc(sizeof(struct _Dir));
690
+
691
+ if (NULL != d) {
692
+ DEBUG_ALLOC(mem_dir, d);
693
+ d->next = g->dirs;
694
+ g->dirs = d;
695
+ d->path = strdup(dir);
696
+ d->plen = (int)strlen(dir);
697
+ DEBUG_ALLOC(mem_dir_path, d->path);
698
+ }
699
+ }