2 ashd - A Sane HTTP Daemon
3 Copyright (C) 2008 Fredrik Tolf <fredrik@dolda2000.com>
5 This program is free software: you can redistribute it and/or modify
6 it under the terms of the GNU General Public License as published by
7 the Free Software Foundation, either version 3 of the License, or
8 (at your option) any later version.
10 This program is distributed in the hope that it will be useful,
11 but WITHOUT ANY WARRANTY; without even the implied warranty of
12 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13 GNU General Public License for more details.
15 You should have received a copy of the GNU General Public License
16 along with this program. If not, see <http://www.gnu.org/licenses/>.
28 #include <arpa/inet.h>
44 unsigned int len, hash;
54 double level, last, etime;
55 typedbuf(struct waiting) brim;
65 double size, rate, retain;
69 static struct bucket *sbuckets[1 << SBUCKETS];
70 static struct bucket **buckets = sbuckets;
71 static int hashlen = SBUCKETS, nbuckets = 0;
72 static typedbuf(struct btime) timeheap;
73 static int child, reload;
75 static const struct config defcfg = {
76 .size = 100, .rate = 10,
77 .retain = 10, .brimsize = 10,
79 static struct config cf;
81 static double rtime(void)
84 static struct timespec or;
87 clock_gettime(CLOCK_MONOTONIC, &ts);
92 return((ts.tv_sec - or.tv_sec) + ((ts.tv_nsec - or.tv_nsec) / 1000000000.0));
95 static struct source reqsource(struct hthead *req)
103 ret = (struct source){};
104 if((sa = getheader(req, "X-Ash-Address")) != NULL) {
105 if(inet_pton(AF_INET, sa, &a4) == 1) {
106 memcpy(ret.data, &a4, ret.len = sizeof(a4));
107 } else if(inet_pton(AF_INET6, sa, &a6) == 1) {
108 memcpy(ret.data, &a6, ret.len = sizeof(a6));
111 for(i = 0, ret.hash = 0; i < ret.len; i++)
112 ret.hash = (ret.hash * 31) + ret.data[i];
116 static void rehash(int nlen)
118 int i, o, n, m, pl, nl;
119 struct bucket **new, **old;
122 if(nlen <= SBUCKETS) {
126 new = szmalloc(sizeof(*new) * (1 << nlen));
131 pl = 1 << hashlen; nl = 1 << nlen; m = nl - 1;
132 for(i = 0; i < pl; i++) {
135 for(o = old[i]->id.hash & m, n = 0; n < nl; o = (o + 1) & m, n++) {
148 static struct bucket *hashget(struct source *src)
150 unsigned int i, n, N, m;
153 m = (N = (1 << hashlen)) - 1;
154 for(i = src->hash & m, n = 0; n < N; i = (i + 1) & m, n++) {
156 if(bk && (bk->id.len == src->len) && !memcmp(bk->id.data, src->data, src->len))
159 for(i = src->hash & m; buckets[i]; i = (i + 1) & m);
160 buckets[i] = bk = szmalloc(sizeof(*bk));
161 memcpy(&bk->id, src, sizeof(*src));
162 bk->last = bk->etime = now;
164 if(++nbuckets > (1 << (hashlen - 1)))
169 static void hashdel(struct bucket *bk)
171 unsigned int i, o, p, n, N, m;
174 m = (N = (1 << hashlen)) - 1;
175 for(i = bk->id.hash & m, n = 0; n < N; i = (i + 1) & m, n++) {
176 assert((sb = buckets[i]) != NULL);
177 if((sb->id.len == bk->id.len) && !memcmp(sb->id.data, bk->id.data, bk->id.len))
182 for(o = (i + 1) & m; buckets[o] != NULL; o = (o + 1) & m) {
184 p = (sb->id.hash - i) & m;
185 if((p == 0) || (p > ((o - i) & m))) {
191 if(--nbuckets <= (1 << (hashlen - 3)))
195 static void thraise(struct btime bt, int n)
201 if(timeheap.b[p].tm <= bt.tm)
203 (timeheap.b[n] = timeheap.b[p]).bk->thpos = n;
206 (timeheap.b[n] = bt).bk->thpos = n;
209 static void thlower(struct btime bt, int n)
214 c2 = (c1 = (n << 1) + 1) + 1;
217 c = ((c2 < timeheap.d) && (timeheap.b[c2].tm < timeheap.b[c1].tm)) ? c2 : c1;
218 if(timeheap.b[c].tm > bt.tm)
220 (timeheap.b[n] = timeheap.b[c]).bk->thpos = n;
223 (timeheap.b[n] = bt).bk->thpos = n;
226 static void thadjust(struct btime bt, int n)
228 if((n > 0) && (timeheap.b[(n - 1) >> 1].tm > bt.tm))
234 static void freebucket(struct bucket *bk)
240 if((n = bk->thpos) >= 0) {
241 r = timeheap.b[--timeheap.d];
245 for(i = 0; i < bk->brim.d; i++) {
246 freehthead(bk->brim.b[i].req);
247 close(bk->brim.b[i].fd);
253 static void updbtime(struct bucket *bk)
257 tm = (bk->level == 0) ? (bk->etime + cf.retain) : (bk->last + (bk->level / cf.rate) + cf.retain);
259 tm2 = bk->last + ((bk->level - cf.size) / cf.rate);
260 tm = (tm2 < tm) ? tm2 : tm;
263 sizebuf(timeheap, ++timeheap.d);
264 thraise((struct btime){bk, tm}, timeheap.d - 1);
266 thadjust((struct btime){bk, tm}, bk->thpos);
270 static void tickbucket(struct bucket *bk)
274 delta = now - bk->last;
277 if((bk->level -= delta * cf.rate) < 0) {
282 while((bk->brim.d > 0) && (bk->level < cf.size)) {
283 if(sendreq(child, bk->brim.b[0].req, bk->brim.b[0].fd)) {
284 flog(LOG_ERR, "ratequeue: could not pass request to child: %s", strerror(errno));
287 freehthead(bk->brim.b[0].req);
288 close(bk->brim.b[0].fd);
294 static void checkbtime(struct bucket *bk)
297 if((bk->level == 0) && (now >= bk->etime + cf.retain)) {
304 static void serve(struct hthead *req, int fd)
310 src = reqsource(req);
313 if(bk->level < cf.size) {
315 if(sendreq(child, req, fd)) {
316 flog(LOG_ERR, "ratequeue: could not pass request to child: %s", strerror(errno));
321 } else if(bk->brim.d < cf.brimsize) {
322 bufadd(bk->brim, ((struct waiting){.req = req, .fd = fd}));
324 simpleerror(fd, 429, "Too many requests", "Your client is being throttled for issuing too frequent requests.");
331 static int parseint(char *str, int *dst)
336 buf = strtol(str, &p, 0);
343 static int parsefloat(char *str, double *dst)
348 buf = strtod(str, &p);
355 static int readconf(char *path, struct config *buf)
361 if((fp = fopen(path, "r")) == NULL) {
362 flog(LOG_ERR, "ratequeue: %s: %s", path, strerror(errno));
366 s = mkcfparser(fp, path);
370 if(!strcmp(s->argv[0], "eof")) {
372 } else if(!strcmp(s->argv[0], "size")) {
373 if((s->argc < 2) || parsefloat(s->argv[1], &buf->size)) {
374 flog(LOG_ERR, "%s:%i: missing or invalid `size' argument");
377 } else if(!strcmp(s->argv[0], "rate")) {
378 if((s->argc < 2) || parsefloat(s->argv[1], &buf->rate)) {
379 flog(LOG_ERR, "%s:%i: missing or invalid `rate' argument");
382 } else if(!strcmp(s->argv[0], "brim")) {
383 if((s->argc < 2) || parseint(s->argv[1], &buf->brimsize)) {
384 flog(LOG_ERR, "%s:%i: missing or invalid `brim' argument");
388 flog(LOG_WARNING, "%s:%i: unknown directive `%s'", s->file, s->lno, s->argv[0]);
398 static void huphandler(int sig)
403 static void usage(FILE *out)
405 fprintf(out, "usage: ratequeue [-h] [-s BUCKET-SIZE] [-r RATE] [-b BRIM-SIZE] PROGRAM [ARGS...]\n");
408 int main(int argc, char **argv)
420 while((c = getopt(argc, argv, "+hc:s:r:b:")) >= 0) {
429 parsefloat(optarg, &cf.size);
432 parsefloat(optarg, &cf.rate);
435 parseint(optarg, &cf.brimsize);
439 if(argc - optind < 1) {
444 if(readconf(cfname, &cfbuf))
448 if((child = stdmkchild(argv + optind, NULL, NULL)) < 0) {
449 flog(LOG_ERR, "ratequeue: could not fork child: %s", strerror(errno));
452 sigaction(SIGHUP, &(struct sigaction){.sa_handler = huphandler}, NULL);
456 if(!readconf(cfname, &cfbuf))
462 pfd = (struct pollfd){.fd = 0, .events = POLLIN};
463 timeout = (timeheap.d > 0) ? timeheap.b[0].tm : -1;
464 if((rv = poll(&pfd, 1, (timeout < 0) ? -1 : (int)((timeout + 0.1 - now) * 1000))) < 0) {
466 flog(LOG_ERR, "ratequeue: error in poll: %s", strerror(errno));
471 if((fd = recvreq(0, &req)) < 0) {
475 flog(LOG_ERR, "recvreq: %s", strerror(errno));
480 while((timeheap.d > 0) && ((now = rtime()) >= timeheap.b[0].tm))
481 checkbtime(timeheap.b[0].bk);