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.
- checksums.yaml +4 -4
- data/CHANGELOG.md +9 -1
- data/README.md +12 -1
- data/ext/agoo/agoo.c +4 -3
- data/ext/agoo/bind.c +51 -11
- data/ext/agoo/bind.h +9 -0
- data/ext/agoo/con.c +127 -89
- data/ext/agoo/con.h +24 -8
- data/ext/agoo/debug.c +2 -0
- data/ext/agoo/debug.h +1 -0
- data/ext/agoo/err.c +1 -1
- data/ext/agoo/err.h +2 -5
- data/ext/agoo/foo/agoo.c +109 -0
- data/ext/agoo/foo/con.c +1220 -0
- data/ext/agoo/foo/con.h +65 -0
- data/ext/agoo/foo/page.c +699 -0
- data/ext/agoo/foo/pub.c +131 -0
- data/ext/agoo/foo/pub.h +40 -0
- data/ext/agoo/foo/rserver.c +1016 -0
- data/ext/agoo/foo/server.c +303 -0
- data/ext/agoo/foo/server.h +67 -0
- data/ext/agoo/foo/upgraded.c +182 -0
- data/ext/agoo/hook.c +31 -0
- data/ext/agoo/hook.h +9 -10
- data/ext/agoo/{types.h → kinds.h} +3 -3
- data/ext/agoo/log.c +168 -83
- data/ext/agoo/log.h +6 -2
- data/ext/agoo/page.c +38 -32
- data/ext/agoo/pub.c +16 -0
- data/ext/agoo/pub.h +1 -0
- data/ext/agoo/queue.h +1 -0
- data/ext/agoo/req.c +95 -0
- data/ext/agoo/req.h +48 -0
- data/ext/agoo/request.c +10 -38
- data/ext/agoo/request.h +1 -34
- data/ext/agoo/res.h +1 -3
- data/ext/agoo/response.c +6 -248
- data/ext/agoo/response.h +2 -6
- data/ext/agoo/rlog.c +2 -1
- data/ext/agoo/rresponse.c +252 -0
- data/ext/agoo/rresponse.h +14 -0
- data/ext/agoo/rserver.c +43 -45
- data/ext/agoo/rserver.h +3 -24
- data/ext/agoo/rupgraded.c +303 -0
- data/ext/agoo/rupgraded.h +17 -0
- data/ext/agoo/server.c +125 -8
- data/ext/agoo/server.h +26 -4
- data/ext/agoo/sse.c +3 -1
- data/ext/agoo/upgraded.c +42 -280
- data/ext/agoo/upgraded.h +16 -8
- data/ext/agoo/websocket.c +13 -9
- data/lib/agoo/base.rb +84 -0
- data/lib/agoo/client.rb +25 -0
- data/lib/agoo/version.rb +1 -1
- data/test/bind_test.rb +2 -2
- metadata +22 -4
data/ext/agoo/foo/con.h
ADDED
@@ -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__ */
|
data/ext/agoo/foo/page.c
ADDED
@@ -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
|
+
}
|