r17063 MediaWiki - Code Review archive

Repository:MediaWiki
Revision:r17062‎ | r17063 | r17064 >
Date:01:36, 17 October 2006
Author:river
Status:old
Tags:
Comment:
add new src
Modified paths:
  • /trunk/willow/src/willow (added) (history)
  • /trunk/willow/src/willow/Makefile.in (added) (history)
  • /trunk/willow/src/willow/confparse.c (added) (history)
  • /trunk/willow/src/willow/daemon.c (added) (history)
  • /trunk/willow/src/willow/lexer.l (added) (history)
  • /trunk/willow/src/willow/parser.y (added) (history)
  • /trunk/willow/src/willow/strlcat.c (added) (history)
  • /trunk/willow/src/willow/strlcpy.c (added) (history)
  • /trunk/willow/src/willow/wbackend.c (added) (history)
  • /trunk/willow/src/willow/wcache.c (added) (history)
  • /trunk/willow/src/willow/wconfig.c (added) (history)
  • /trunk/willow/src/willow/whttp.c (added) (history)
  • /trunk/willow/src/willow/whttp_entity.c (added) (history)
  • /trunk/willow/src/willow/willow.c (added) (history)
  • /trunk/willow/src/willow/wlogwriter.c (added) (history)
  • /trunk/willow/src/wlogwriter (added) (history)
  • /trunk/willow/src/wlogwriter/Makefile.in (added) (history)
  • /trunk/willow/src/wlogwriter/wlogwriter.c (added) (history)

Diff [purge]

Index: trunk/willow/src/wlogwriter/Makefile.in
@@ -0,0 +1,8 @@
 2+PROGRAM = wlogwriter
 3+
 4+SRCS = wlogwriter.c
 5+BINDIR=@libexecdir@
 6+
 7+EXTRA_DIST=Makefile.in
 8+
 9+@include@ @q@@top_srcdir@/mk/prog.mk@q@
Index: trunk/willow/src/wlogwriter/wlogwriter.c
@@ -0,0 +1,58 @@
 2+/* @(#) $Header$ */
 3+/* This source code is in the public domain. */
 4+/*
 5+ * Willow: Lightweight HTTP reverse-proxy.
 6+ * wlogwriter: child process for log writing.
 7+ */
 8+
 9+#if defined __SUNPRO_C || defined __DECC || defined __HP_cc
 10+# pragma ident "@(#)$Header$"
 11+#endif
 12+
 13+#include <stdio.h>
 14+#include <unistd.h>
 15+#include <errno.h>
 16+#include <string.h>
 17+#include <stdlib.h>
 18+#include <signal.h>
 19+
 20+#include "config.h"
 21+
 22+int
 23+main(argc, argv)
 24+ int argc;
 25+ char **argv;
 26+{
 27+ FILE *outf;
 28+ char *line;
 29+ size_t lnsz;
 30+
 31+ lnsz = 8192;
 32+ line = malloc(lnsz);
 33+
 34+ (void)signal(SIGPIPE, SIG_IGN);
 35+
 36+ if (argc < 2) {
 37+ (void)fprintf(stderr, "not enough arguments\n");
 38+ exit(8);
 39+ }
 40+
 41+#ifdef HAVE_SETPROCTITLE
 42+ setproctitle("log writer: %s", argv[1]);
 43+#endif
 44+
 45+ /*LINTED unsafe fopen*/
 46+ if ((outf = fopen(argv[1], "a")) == NULL) {
 47+ perror(argv[1]);
 48+ exit(8);
 49+ }
 50+
 51+ while (fgets(line, lnsz, stdin)) {
 52+ if (fputs(line, outf) == EOF || fflush(outf) == EOF) {
 53+ exit(8);
 54+ }
 55+ }
 56+
 57+ exit(0);
 58+ /*NOTREACHED*/
 59+}
Index: trunk/willow/src/willow/wbackend.c
@@ -0,0 +1,321 @@
 2+/* @(#) $Header$ */
 3+/* This source code is in the public domain. */
 4+/*
 5+ * Willow: Lightweight HTTP reverse-proxy.
 6+ * wbackend: HTTP backend handling.
 7+ */
 8+
 9+#if defined __SUNPRO_C || defined __DECC || defined __HP_cc
 10+# pragma ident "@(#)$Header$"
 11+#endif
 12+
 13+#include <sys/types.h>
 14+#include <sys/socket.h>
 15+
 16+#include <arpa/inet.h>
 17+
 18+#include <stdlib.h>
 19+#include <stdio.h>
 20+#include <string.h>
 21+#include <errno.h>
 22+#include <strings.h>
 23+#include <limits.h>
 24+#include <math.h>
 25+#include <time.h>
 26+
 27+#include "willow.h"
 28+#include "wbackend.h"
 29+#include "wnet.h"
 30+#include "wlog.h"
 31+#include "confparse.h"
 32+#include "wconfig.h"
 33+
 34+#define rotl(i,r) (((i) << (r)) | ((i) >> (sizeof(i)*CHAR_BIT-(r))))
 35+
 36+static struct backend **backends;
 37+int nbackends;
 38+
 39+static struct backend *new_backend(const char *, const char *, int);
 40+static void backend_read(struct fde *);
 41+static struct backend *next_backend(const char *url);
 42+static uint32_t carp_urlhash(const char *);
 43+static uint32_t carp_hosthash(const char *);
 44+static uint32_t carp_combine(const char *url, uint32_t host);
 45+static void carp_recalc(const char *url);
 46+static void carp_calc(void);
 47+static int becalc_cmp(const void *a, const void *b);
 48+
 49+struct backend_cb_data {
 50+struct backend *bc_backend;
 51+ backend_cb bc_func;
 52+ void *bc_data;
 53+const char *bc_url;
 54+};
 55+
 56+static struct backend *
 57+new_backend(name, addr, port)
 58+ const char *name, *addr;
 59+ int port;
 60+{
 61+struct backend *nb;
 62+
 63+ if ((nb = wcalloc(1, sizeof(*nb))) == NULL)
 64+ outofmemory();
 65+
 66+ nb->be_port = port;
 67+ nb->be_straddr = wstrdup(addr);
 68+ nb->be_name = wstrdup(name);
 69+ nb->be_addr.sin_family = AF_INET;
 70+ nb->be_addr.sin_port = htons(nb->be_port);
 71+ nb->be_addr.sin_addr.s_addr = inet_addr(nb->be_name);
 72+ nb->be_dead = 0;
 73+ nb->be_hash = carp_hosthash(nb->be_name);
 74+ nb->be_load = 1.0;
 75+ return nb;
 76+}
 77+
 78+void
 79+add_backend(addr, port)
 80+ const char *addr;
 81+ int port;
 82+{
 83+ if (port < 1 || port > 65535) {
 84+ conf_report_error("invalid backend port: %d", port);
 85+ nerrors++;
 86+ return;
 87+ }
 88+
 89+ if ((backends = wrealloc(backends, sizeof(struct backend*) * ++nbackends)) == NULL)
 90+ outofmemory();
 91+ backends[nbackends - 1] = new_backend(addr, addr, port);
 92+ carp_calc();
 93+ wlog(WLOG_NOTICE, "backend: %s:%d", addr, port);
 94+}
 95+
 96+#if 0
 97+void
 98+backend_file(file)
 99+ char *file;
 100+{
 101+ FILE *f;
 102+ char line[1024];
 103+
 104+ if ((f = fopen(file, "r")) == NULL) {
 105+ perror(file);
 106+ exit(8);
 107+ }
 108+
 109+ while (fgets(line, sizeof line, f)) {
 110+ line[strlen(line) - 1] = '\0';
 111+ add_backend(line);
 112+ }
 113+
 114+ (void)fclose(f);
 115+}
 116+#endif
 117+
 118+int
 119+get_backend(url, func, data, flags)
 120+ const char *url;
 121+ backend_cb func;
 122+ void *data;
 123+ int flags;
 124+{
 125+struct backend_cb_data *cbd;
 126+ int s;
 127+
 128+ WDEBUG((WLOG_DEBUG, "get_backend: called"));
 129+
 130+ if ((cbd = wmalloc(sizeof(*cbd))) == NULL)
 131+ outofmemory();
 132+
 133+ cbd->bc_func = func;
 134+ cbd->bc_data = data;
 135+ cbd->bc_url = url;
 136+
 137+ for (;;) {
 138+ cbd->bc_backend = next_backend(url);
 139+
 140+ if (cbd->bc_backend == NULL) {
 141+ wfree(cbd);
 142+ return -1;
 143+ }
 144+
 145+ if ((s = wnet_open("backend connection")) == -1) {
 146+ wlog(WLOG_WARNING, "opening backend socket: %s", strerror(errno));
 147+ wfree(cbd);
 148+ return -1;
 149+ }
 150+
 151+ if (connect(s, (struct sockaddr *)&cbd->bc_backend->be_addr,
 152+ sizeof(cbd->bc_backend->be_addr)) == 0) {
 153+ WDEBUG((WLOG_DEBUG, "get_backend: connection completed immediately"));
 154+ func(cbd->bc_backend, &fde_table[s], data);
 155+ wfree(cbd);
 156+ return 0;
 157+ }
 158+
 159+ if (errno != EINPROGRESS) {
 160+ time_t retry = time(NULL) + config.backend_retry;
 161+ wnet_close(s);
 162+ wlog(WLOG_WARNING, "%s: %s; retry in %d seconds",
 163+ cbd->bc_backend->be_name, strerror(errno), config.backend_retry);
 164+ cbd->bc_backend->be_dead = 1;
 165+ cbd->bc_backend->be_time = retry;
 166+ continue;
 167+ }
 168+
 169+ WDEBUG((WLOG_DEBUG, "get_backend: waiting for connection to complete"));
 170+ wnet_register(s, FDE_WRITE, backend_read, cbd);
 171+ return 0;
 172+ }
 173+}
 174+
 175+static void
 176+backend_read(e)
 177+ struct fde *e;
 178+{
 179+struct backend_cb_data *cbd = e->fde_rdata;
 180+ int error = 0;
 181+ socklen_t len = sizeof(error);
 182+
 183+ getsockopt(e->fde_fd, SOL_SOCKET, SO_ERROR, &error, &len);
 184+
 185+ if (error && error != EINPROGRESS) {
 186+ time_t retry = time(NULL) + config.backend_retry;
 187+ wnet_close(e->fde_fd);
 188+ wlog(WLOG_WARNING, "%s: [%d] %s; retry in %d seconds",
 189+ cbd->bc_backend->be_name, error, strerror(error), config.backend_retry);
 190+ cbd->bc_backend->be_dead = 1;
 191+ cbd->bc_backend->be_time = time(NULL) + config.backend_retry;
 192+ if (get_backend(cbd->bc_url, cbd->bc_func, cbd->bc_data, 0) == -1) {
 193+ cbd->bc_func(NULL, NULL, cbd->bc_data);
 194+ }
 195+
 196+ wfree(cbd);
 197+ return;
 198+ }
 199+
 200+ /*
 201+ * After handing the fd off to the caller, we don't care about it
 202+ * any more.
 203+ */
 204+ wnet_register(e->fde_fd, FDE_WRITE, NULL, NULL);
 205+
 206+ cbd->bc_func(cbd->bc_backend, e, cbd->bc_data);
 207+ wfree(cbd);
 208+}
 209+
 210+static struct backend *
 211+next_backend(url)
 212+ const char *url;
 213+{
 214+static int cur = 0;
 215+ int tried = 0;
 216+
 217+ if (config.use_carp) {
 218+ carp_recalc(url);
 219+ // cur = 0;
 220+ }
 221+
 222+ WDEBUG((WLOG_DEBUG, "next_backend: url=[%s]", url));
 223+
 224+ while (tried++ <= nbackends) {
 225+ time_t now = time(NULL);
 226+
 227+ if (cur >= nbackends)
 228+ cur = 0;
 229+
 230+ if (backends[cur]->be_dead && now >= backends[cur]->be_time)
 231+ backends[cur]->be_dead = 0;
 232+
 233+ if (backends[cur]->be_dead) {
 234+ cur++;
 235+ continue;
 236+ }
 237+
 238+ if (config.use_carp)
 239+ cur = 0;
 240+ return backends[cur++];
 241+ }
 242+
 243+ return NULL;
 244+}
 245+
 246+static uint32_t
 247+carp_urlhash(str)
 248+ const char *str;
 249+{
 250+ const char *ostr = str;
 251+ uint32_t h = 0;
 252+ for (; *str; ++str)
 253+ h += (rotl(h, 19) + *str);
 254+ WDEBUG((WLOG_DEBUG, "hash(%s) = %d", ostr, h));
 255+ return h;
 256+}
 257+
 258+static uint32_t
 259+carp_hosthash(str)
 260+ const char *str;
 261+{
 262+ uint32_t h = carp_urlhash(str) * 0x62531965;
 263+ return rotl(h, 21);
 264+}
 265+
 266+static uint32_t
 267+carp_combine(url, host)
 268+ const char *url;
 269+ uint32_t host;
 270+{
 271+ uint32_t c = carp_urlhash(url) ^ host;
 272+ c += c * 0x62531965;
 273+ return rotl(c, 21);
 274+}
 275+
 276+static void
 277+carp_calc(void)
 278+{
 279+struct backend *be, *prev;
 280+ int i, j;
 281+
 282+ backends[0]->be_carp = pow((nbackends * backends[0]->be_load), 1.0 / nbackends);
 283+ backends[0]->be_carplfm = 1.0;
 284+ for (i = 1; i < nbackends; ++i) {
 285+ float l = 0;
 286+ be = backends[i];
 287+ prev = backends[i - 1];
 288+ be->be_carplfm = 1.0 + ((nbackends-i+1) * (be->be_load - prev->be_load));
 289+ WDEBUG((WLOG_DEBUG, "carp_calc: %s lfm = %f", be->be_name, be->be_carplfm));
 290+ for (j = 0; j < i; ++j)
 291+ l *= backends[j]->be_carp;
 292+ be->be_carp /= l;
 293+ be->be_carp += pow(prev->be_carp, nbackends-i+1);
 294+ be->be_carp = pow(be->be_carp, 1/(nbackends-i+1));
 295+ }
 296+}
 297+
 298+static void
 299+carp_recalc(url)
 300+ const char *url;
 301+{
 302+ uint32_t hash;
 303+ int i;
 304+ for (i = 0; i < nbackends; ++i) {
 305+ hash = carp_urlhash(url) ^ backends[i]->be_hash;
 306+ hash += hash * 0x62531965;
 307+ hash = rotl(hash, 21);
 308+ hash *= backends[i]->be_carplfm;
 309+ WDEBUG((WLOG_DEBUG, "%s: hash = %lu, lfm = %f", backends[i]->be_name, hash, backends[i]->be_carplfm));
 310+ backends[i]->be_carp = hash;
 311+ }
 312+ qsort(backends, nbackends, sizeof(struct backend *), becalc_cmp);
 313+ WDEBUG((WLOG_DEBUG, "first backend = %s", backends[0]->be_name));
 314+}
 315+
 316+static int
 317+becalc_cmp(a, b)
 318+const void *a, *b;
 319+{
 320+struct backend **ba = a, **bb = b;
 321+ return (*ba)->be_carp - (*bb)->be_carp;
 322+}
Index: trunk/willow/src/willow/confparse.c
@@ -0,0 +1,515 @@
 2+/* @(#) $Header$ */
 3+/* From ircd-ratbox: newconf.c,v 7.209 2005/04/05 01:22:57 leeh Exp */
 4+/* This source code is in the public domain. */
 5+/*
 6+ * Willow: Lightweight HTTP reverse-proxy.
 7+ * confparse: config parser implementation.
 8+ */
 9+
 10+#if defined __SUNPRO_C || defined __DECC || defined __HP_cc
 11+# pragma ident "@(#)$Header$"
 12+#endif
 13+
 14+#include <stdarg.h>
 15+#include <stdio.h>
 16+#include <string.h>
 17+#include <syslog.h>
 18+
 19+#include "willow.h"
 20+#include "confparse.h"
 21+#include "queue.h"
 22+#include "wlog.h"
 23+#include "wbackend.h"
 24+#include "wconfig.h"
 25+
 26+#define CF_TYPE(x) ((x) & CF_MTYPE)
 27+
 28+int nerrors;
 29+struct top_conf *conf_cur_block;
 30+char *conf_cur_block_name;
 31+
 32+static LIST_HEAD(conf_items_head, top_conf) conf_items =
 33+ LIST_HEAD_INITIALIZER(conf_items);
 34+
 35+static struct conf_entry *find_conf_item(struct top_conf *top, const char *);
 36+static void conf_set_generic_int(void *, void *);
 37+static void conf_set_generic_string(void *, int, void *);
 38+static void add_top_conf(const char *name, int (*) (struct top_conf *),
 39+ int (*) (struct top_conf *), struct conf_entry *);
 40+
 41+void
 42+conf_report_error(const char *fmt, ...)
 43+{
 44+ char buf[1024];
 45+ va_list ap;
 46+
 47+ va_start(ap, fmt);
 48+ (void)vsnprintf(buf, 1024, fmt, ap);
 49+ va_end(ap);
 50+
 51+ wlog(WLOG_ERROR, "\"%s\", line %d: %s", current_file, lineno, buf);
 52+}
 53+
 54+void
 55+yyerror(msg)
 56+ const char *msg;
 57+{
 58+ conf_report_error("%s", msg);
 59+}
 60+
 61+static const char *
 62+conf_strtype(type)
 63+ int type;
 64+{
 65+ switch (type & CF_MTYPE)
 66+ {
 67+ case CF_INT:
 68+ return "integer value";
 69+ case CF_STRING:
 70+ return "unquoted string";
 71+ case CF_YESNO:
 72+ return "yes/no value";
 73+ case CF_QSTRING:
 74+ return "quoted string";
 75+ case CF_TIME:
 76+ return "time/size value";
 77+ default:
 78+ return "unknown type";
 79+ }
 80+}
 81+
 82+static void
 83+add_top_conf(name, sfunc, efunc, items)
 84+ const char *name;
 85+ int (*sfunc) (struct top_conf *);
 86+ int (*efunc) (struct top_conf *);
 87+ struct conf_entry *items;
 88+{
 89+struct top_conf *tc;
 90+
 91+ tc = wmalloc(sizeof(struct top_conf));
 92+
 93+ tc->tc_name = wstrdup(name);
 94+ tc->tc_sfunc = sfunc;
 95+ tc->tc_efunc = efunc;
 96+ tc->tc_entries = items;
 97+
 98+ LIST_INSERT_HEAD(&conf_items, tc, entries);
 99+}
 100+
 101+static struct top_conf *
 102+find_top_conf(name)
 103+ const char *name;
 104+{
 105+struct top_conf *tc;
 106+
 107+ LIST_FOREACH(tc, &conf_items, entries) {
 108+ if (!strcasecmp(tc->tc_name, name))
 109+ return tc;
 110+ }
 111+
 112+ return NULL;
 113+}
 114+
 115+static struct conf_entry *
 116+find_conf_item(top, name)
 117+ struct top_conf *top;
 118+ const char *name;
 119+{
 120+struct conf_entry *cf;
 121+
 122+ if (top->tc_entries) {
 123+ int i;
 124+
 125+ for(i = 0; top->tc_entries[i].cf_type; i++)
 126+ {
 127+ cf = &top->tc_entries[i];
 128+
 129+ if(!strcasecmp(cf->cf_name, name))
 130+ return cf;
 131+ }
 132+ }
 133+
 134+ LIST_FOREACH(cf, &top->tc_items, entries) {
 135+ if(strcasecmp(cf->cf_name, name) == 0)
 136+ return cf;
 137+ }
 138+
 139+ return NULL;
 140+}
 141+
 142+/*ARGSUSED*/
 143+int
 144+conf_call_set(tc, item, value, type)
 145+ struct top_conf *tc;
 146+ char *item;
 147+ conf_parm_t *value;
 148+ int type;
 149+{
 150+struct conf_entry *cf;
 151+ conf_parm_t *cp;
 152+
 153+ if (!tc)
 154+ return -1;
 155+
 156+ if ((cf = find_conf_item(tc, item)) == NULL) {
 157+ conf_report_error("Non-existant configuration setting %s::%s.",
 158+ tc->tc_name, (char *) item);
 159+ nerrors++;
 160+ return -1;
 161+ }
 162+
 163+ /*
 164+ * If it takes one thing, make sure they only passed one thing,
 165+ * and handle as needed.
 166+ */
 167+ if (value->type & CF_FLIST && !cf->cf_type & CF_FLIST) {
 168+ conf_report_error("Option %s::%s does not take a list of values.",
 169+ tc->tc_name, item);
 170+ nerrors++;
 171+ return -1;
 172+ }
 173+
 174+ cp = value->v.list;
 175+
 176+ if (CF_TYPE(value->v.list->type) != CF_TYPE(cf->cf_type)) {
 177+ /*
 178+ * If it expects a string value, but we got a yesno,
 179+ * convert it back
 180+ */
 181+ if((CF_TYPE(value->v.list->type) == CF_YESNO) &&
 182+ (CF_TYPE(cf->cf_type) == CF_STRING)) {
 183+ value->v.list->type = CF_STRING;
 184+
 185+ if(cp->v.number == 1)
 186+ cp->v.string = wstrdup("yes");
 187+ else
 188+ cp->v.string = wstrdup("no");
 189+ }
 190+
 191+ /*
 192+ * Maybe it's a CF_TIME and they passed CF_INT --
 193+ * should still be valid.
 194+ */
 195+ else if(!((CF_TYPE(value->v.list->type) == CF_INT) &&
 196+ (CF_TYPE(cf->cf_type) == CF_TIME))) {
 197+ conf_report_error("Wrong type for %s::%s (expected %s, got %s)",
 198+ tc->tc_name, (char *) item,
 199+ conf_strtype(cf->cf_type), conf_strtype(value->v.list->type));
 200+ nerrors++;
 201+ return -1;
 202+ }
 203+ }
 204+
 205+ if (cf->cf_type & CF_FLIST) {
 206+ /* just pass it the extended argument list */
 207+ cf->cf_func(value->v.list);
 208+ } else {
 209+ /* it's old-style, needs only one arg */
 210+ switch (cf->cf_type) {
 211+ case CF_INT:
 212+ case CF_TIME:
 213+ case CF_YESNO:
 214+ if(cf->cf_arg)
 215+ conf_set_generic_int(&cp->v.number, cf->cf_arg);
 216+ else
 217+ cf->cf_func(&cp->v.number);
 218+ break;
 219+ case CF_STRING:
 220+ case CF_QSTRING:
 221+ if(!*cp->v.string)
 222+ conf_report_error("Ignoring %s::%s -- empty field",
 223+ tc->tc_name, item);
 224+ else if(cf->cf_arg)
 225+ conf_set_generic_string(cp->v.string, cf->cf_len, cf->cf_arg);
 226+ else
 227+ cf->cf_func(cp->v.string);
 228+ break;
 229+ }
 230+ }
 231+
 232+ return 0;
 233+}
 234+
 235+int
 236+conf_start_block(block, name)
 237+ const char *block, *name;
 238+{
 239+ if ((conf_cur_block = find_top_conf(block)) == NULL) {
 240+ conf_report_error("Configuration block '%s' is not defined.", block);
 241+ nerrors++;
 242+ return -1;
 243+ }
 244+
 245+ if (name)
 246+ conf_cur_block_name = wstrdup(name);
 247+ else
 248+ conf_cur_block_name = NULL;
 249+
 250+ if (conf_cur_block->tc_sfunc)
 251+ if (conf_cur_block->tc_sfunc(conf_cur_block) < 0)
 252+ return -1;
 253+
 254+ return 0;
 255+}
 256+
 257+int
 258+conf_end_block(tc)
 259+ struct top_conf *tc;
 260+{
 261+ if(tc->tc_efunc)
 262+ return tc->tc_efunc(tc);
 263+
 264+ wfree(conf_cur_block_name);
 265+ return 0;
 266+}
 267+
 268+
 269+static void
 270+conf_set_generic_int(data, location)
 271+ void *data, *location;
 272+{
 273+ *((int *) location) = *((unsigned int *) data);
 274+}
 275+
 276+static void
 277+conf_set_generic_string(data, len, location)
 278+ void *data, *location;
 279+ int len;
 280+{
 281+ char **loc = location;
 282+ char *input = data;
 283+
 284+ if(len && strlen(input) > len)
 285+ input[len] = '\0';
 286+
 287+ wfree(*loc);
 288+ *loc = wstrdup(input);
 289+}
 290+
 291+static int backend_port;
 292+
 293+/*ARGSUSED*/
 294+static int
 295+conf_begin_backend(tc)
 296+ struct top_conf *tc;
 297+{
 298+ if (conf_cur_block_name == NULL) {
 299+ conf_report_error("backend name not specified");
 300+ nerrors++;
 301+ return -1;
 302+ }
 303+
 304+ return 0;
 305+}
 306+
 307+/*ARGSUSED*/
 308+static int
 309+conf_end_backend(tc)
 310+ struct top_conf *tc;
 311+{
 312+ add_backend(conf_cur_block_name, backend_port);
 313+ backend_port = 0;
 314+
 315+ return 0;
 316+}
 317+
 318+static int listen_port;
 319+
 320+/*ARGSUSED*/
 321+static int
 322+conf_begin_listen(tc)
 323+ struct top_conf *tc;
 324+{
 325+ if (conf_cur_block_name == NULL) {
 326+ conf_report_error("listener host not specified");
 327+ nerrors++;
 328+ return -1;
 329+ }
 330+
 331+ return 0;
 332+}
 333+
 334+/*ARGSUSED*/
 335+static int
 336+conf_end_listen(tc)
 337+ struct top_conf *tc;
 338+{
 339+ add_listener(conf_cur_block_name, listen_port);
 340+ listen_port = 0;
 341+
 342+ return 0;
 343+}
 344+
 345+static int cachedir_size;
 346+
 347+/*ARGSUSED*/
 348+static int
 349+conf_begin_cachedir(tc)
 350+ struct top_conf *tc;
 351+{
 352+ if (conf_cur_block_name == NULL) {
 353+ conf_report_error("cache directory not specified");
 354+ nerrors++;
 355+ return -1;
 356+ }
 357+
 358+ return 0;
 359+}
 360+
 361+/*ARGSUSED*/
 362+static int
 363+conf_end_cachedir(tc)
 364+ struct top_conf *tc;
 365+{
 366+ add_cachedir(conf_cur_block_name, cachedir_size);
 367+ cachedir_size = 0;
 368+
 369+ return 0;
 370+}
 371+
 372+static char *log_file, *log_syslog_facility, *log_access_log;
 373+static int log_level, log_syslog;
 374+
 375+static struct syslog_facility {
 376+ char *name;
 377+ int fac;
 378+} syslog_facilities[] = {
 379+ {"user", LOG_USER},
 380+ {"mail", LOG_MAIL},
 381+ {"daemon", LOG_DAEMON},
 382+ {"auth", LOG_AUTH},
 383+ {"lpr", LOG_LPR},
 384+ {"news", LOG_NEWS},
 385+ {"uucp", LOG_UUCP},
 386+ {"cron", LOG_CRON},
 387+#ifdef LOG_AUDIT
 388+ {"audit", LOG_AUDIT},
 389+#endif
 390+ {"local0", LOG_LOCAL0},
 391+ {"local1", LOG_LOCAL0},
 392+ {"local2", LOG_LOCAL0},
 393+ {"local3", LOG_LOCAL0},
 394+ {"local4", LOG_LOCAL0},
 395+ {"local5", LOG_LOCAL0},
 396+ {"local6", LOG_LOCAL0},
 397+ {"local7", LOG_LOCAL0},
 398+ {NULL, 0},
 399+};
 400+
 401+/*ARGSUSED*/
 402+static int
 403+conf_end_log(tc)
 404+ struct top_conf *tc;
 405+{
 406+ if (log_file) {
 407+ logging.file = wstrdup(log_file);
 408+ }
 409+
 410+ if (log_syslog) {
 411+ struct syslog_facility *fac = syslog_facilities;
 412+
 413+ logging.syslog = 1;
 414+
 415+ if (log_syslog_facility) {
 416+ for (; fac->name; fac++) {
 417+ if (!strcmp(fac->name, log_syslog_facility)) {
 418+ logging.facility = fac->fac;
 419+ break;
 420+ }
 421+ }
 422+ if (!fac->name) {
 423+ conf_report_error("unrecognised syslog facility \"%s\"", log_syslog_facility);
 424+ nerrors++;
 425+ }
 426+ } else
 427+ logging.facility = LOG_DAEMON;
 428+ }
 429+
 430+ logging.level = log_level;
 431+ if (log_access_log)
 432+ config.access_log = wstrdup(log_access_log);
 433+ return 0;
 434+}
 435+
 436+static int cache_expire_threshold = 25;
 437+static int cache_expire_every = 60;
 438+static int cache_compress, cache_complevel = 6, cache_private, cache_use_carp;
 439+static int backend_retry;
 440+char *cache_user, *cache_group;
 441+
 442+static int
 443+conf_end_cache(tc)
 444+ struct top_conf *tc;
 445+{
 446+ if (cache_expire_threshold < 0 || cache_expire_threshold > 100) {
 447+ conf_report_error("cache::expire_threshold must be between 0 and 100");
 448+ nerrors++;
 449+ }
 450+ if (cache_expire_every < 0) {
 451+ conf_report_error("cache::expire_every must be greater than 0");
 452+ nerrors++;
 453+ }
 454+
 455+ config.cache_expevery = cache_expire_every;
 456+ config.cache_expthresh = cache_expire_threshold;
 457+ config.suid = cache_user;
 458+ config.sgid = cache_group;
 459+ config.compress = cache_compress;
 460+ if (cache_complevel < 1 || cache_complevel > 9) {
 461+ conf_report_error("cache::compress_level must be between 1 and 9");
 462+ nerrors++;
 463+ }
 464+ config.complevel = cache_complevel;
 465+ config.backend_retry = backend_retry;
 466+ config.cache_private = cache_private;
 467+ config.use_carp = cache_use_carp;
 468+ return 0;
 469+}
 470+
 471+static struct conf_entry conf_backend_table[] = {
 472+ { "port", CF_INT, NULL, 0, &backend_port },
 473+ { NULL, 0, NULL, 0, NULL }
 474+};
 475+
 476+static struct conf_entry conf_listen_table[] = {
 477+ { "port", CF_INT, NULL, 0, &listen_port },
 478+ { NULL, 0, NULL, 0, NULL }
 479+};
 480+
 481+static struct conf_entry conf_cachedir_table[] = {
 482+ { "size", CF_TIME, NULL, 0, &cachedir_size },
 483+ { NULL, 0, NULL, 0, NULL }
 484+};
 485+
 486+static struct conf_entry conf_log_table[] = {
 487+ { "level", CF_INT, NULL, 0, &log_level },
 488+ { "file", CF_QSTRING, NULL, 0, &log_file },
 489+ { "syslog", CF_YESNO, NULL, 0, &log_syslog },
 490+ { "facility", CF_STRING, NULL, 0, &log_syslog_facility },
 491+ { "access-log", CF_QSTRING, NULL, 0, &log_access_log },
 492+ { NULL, 0, NULL, 0, NULL }
 493+};
 494+
 495+static struct conf_entry conf_cache_table[] = {
 496+ { "expire_every", CF_TIME, NULL, 0, &cache_expire_every },
 497+ { "expire_threshold", CF_INT, NULL, 0, &cache_expire_threshold },
 498+ { "user", CF_STRING, NULL, 0, &cache_user },
 499+ { "group", CF_STRING, NULL, 0, &cache_group },
 500+ { "compress", CF_YESNO, NULL, 0, &cache_compress },
 501+ { "compress_level", CF_INT, NULL, 0, &cache_complevel },
 502+ { "backend_retry", CF_TIME, NULL, 0, &backend_retry },
 503+ { "cache_private", CF_YESNO, NULL, 0, &cache_private },
 504+ { "use_carp", CF_YESNO, NULL, 0, &cache_use_carp },
 505+ { NULL, 0, NULL, 0, NULL }
 506+};
 507+
 508+void
 509+newconf_init(void)
 510+{
 511+ add_top_conf("backend", conf_begin_backend, conf_end_backend, conf_backend_table);
 512+ add_top_conf("listen", conf_begin_listen, conf_end_listen, conf_listen_table);
 513+ add_top_conf("cache-dir", conf_begin_cachedir, conf_end_cachedir, conf_cachedir_table);
 514+ add_top_conf("log", NULL, conf_end_log, conf_log_table);
 515+ add_top_conf("cache", NULL, conf_end_cache, conf_cache_table);
 516+}
Index: trunk/willow/src/willow/strlcpy.c
@@ -0,0 +1,61 @@
 2+/* @(#) $Header$ */
 3+/* $NetBSD: strlcpy.c,v 1.14 2003/10/27 00:12:42 lukem Exp $ */
 4+/* $OpenBSD: strlcpy.c,v 1.7 2003/04/12 21:56:39 millert Exp $ */
 5+
 6+/*
 7+ * Copyright (c) 1998 Todd C. Miller <Todd.Miller@courtesan.com>
 8+ *
 9+ * Permission to use, copy, modify, and distribute this software for any
 10+ * purpose with or without fee is hereby granted, provided that the above
 11+ * copyright notice and this permission notice appear in all copies.
 12+ *
 13+ * THE SOFTWARE IS PROVIDED "AS IS" AND TODD C. MILLER DISCLAIMS ALL
 14+ * WARRANTIES WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES
 15+ * OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL TODD C. MILLER BE LIABLE
 16+ * FOR ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
 17+ * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION
 18+ * OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN
 19+ * CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
 20+ */
 21+
 22+#if defined __SUNPRO_C || defined __DECC || defined __HP_cc
 23+# pragma ident "@(#) $Header$"
 24+#endif
 25+
 26+#include <sys/types.h>
 27+#include <assert.h>
 28+#include <string.h>
 29+
 30+/*
 31+ * Copy src to string dst of size siz. At most siz-1 characters
 32+ * will be copied. Always NUL terminates (unless siz == 0).
 33+ * Returns strlen(src); if retval >= siz, truncation occurred.
 34+ */
 35+size_t
 36+strlcpy(dst, src, siz)
 37+ char *dst;
 38+ const char *src;
 39+ size_t siz;
 40+{
 41+ char *d = dst;
 42+ const char *s = src;
 43+ size_t n = siz;
 44+
 45+ /* Copy as many bytes as will fit */
 46+ if (n != 0 && --n != 0) {
 47+ do {
 48+ if ((*d++ = *s++) == 0)
 49+ break;
 50+ } while (--n != 0);
 51+ }
 52+
 53+ /* Not enough room in dst, add NUL and traverse rest of src */
 54+ if (n == 0) {
 55+ if (siz != 0)
 56+ *d = '\0'; /* NUL-terminate dst */
 57+ while (*s++)
 58+ ;
 59+ }
 60+
 61+ return(s - src - 1); /* count does not include NUL */
 62+}
Index: trunk/willow/src/willow/Makefile.in
@@ -0,0 +1,30 @@
 2+PROGRAM = willow
 3+BINDIR = @bindir@
 4+CPPFLAGS = -I../include
 5+
 6+BASESRCS = willow.c wconfig.c whttp.c wbackend.c whttp_entity.c wcache.c confparse.c
 7+SRCS=$(BASESRCS)
 8+OBJADD = @LIBOBJS@ y.tab.o lex.yy.o
 9+
 10+EXTRA_DIST= strlcat.c strlcpy.c \
 11+ lexer.l parser.y \
 12+ daemon.c \
 13+ Makefile.in
 14+
 15+LDFLAGS = -L$(SRCROOT)/src/lib/wnet -lwillownet \
 16+ -L$(SRCROOT)/src/lib/wlog -lwillowlog \
 17+ $(LIBOBJS)
 18+
 19+default: all
 20+
 21+y.tab.o: y.tab.c
 22+lex.yy.o: lex.yy.c y.tab.h
 23+
 24+y.tab.c y.tab.h: parser.y
 25+ @echo " $(_YACC) -d parser.y"
 26+ @$(_YACC) -d parser.y
 27+lex.yy.c: lexer.l y.tab.h
 28+ @echo " $(_LEX) lexer.l"
 29+ @$(_LEX) lexer.l || rm -f lex.yy.c
 30+
 31+@include@ @q@@top_srcdir@/mk/prog.mk@q@
Index: trunk/willow/src/willow/parser.y
@@ -0,0 +1,317 @@
 2+/* @(#) $Header$ */
 3+/* From: $Nightmare: nightmare/src/main/parser.y,v 1.2.2.1.2.1 2002/07/02 03:42:10 ejb Exp $ */
 4+/* From: ircd_parser.y,v 1.281 2005/03/22 16:27:48 androsyn Exp */
 5+/* This source code is in the public domain. */
 6+/*
 7+ * Willow: Lightweight HTTP reverse-proxy.
 8+ * parser: configuration file parser.
 9+ */
 10+
 11+%{
 12+#if defined __SUNPRO_CC || defined __DECC || defined __HP_cc
 13+# pragma ident "@(#)$Header$"
 14+#endif
 15+
 16+#include <sys/types.h>
 17+#include <sys/stat.h>
 18+
 19+#include <netinet/in.h>
 20+
 21+#include <string.h>
 22+#include <stdlib.h>
 23+#include <stdarg.h>
 24+#include <stdio.h>
 25+
 26+#include "willow.h"
 27+#include "confparse.h"
 28+
 29+#define YY_NO_UNPUT
 30+
 31+int yyparse();
 32+int yyerror(const char *);
 33+int yylex();
 34+
 35+static time_t conf_find_time(const char*);
 36+static void add_cur_list(int, const char *, int);
 37+
 38+static struct {
 39+ const char * name;
 40+ const char * plural;
 41+ time_t val;
 42+} conf_times[] = {
 43+ {"second", "seconds", 1},
 44+ {"minute", "minutes", 60},
 45+ {"hour", "hours", 60 * 60},
 46+ {"day", "days", 60 * 60 * 24},
 47+ {"week", "weeks", 60 * 60 * 24 * 7},
 48+ {"fortnight", "fortnights", 60 * 60 * 24 * 14},
 49+ {"month", "months", 60 * 60 * 24 * 7 * 4},
 50+ {"year", "years", 60 * 60 * 24 * 365},
 51+ /* ok-- we now do sizes here too. they aren't times, but
 52+ it's close enough */
 53+ {"byte", "bytes", 1},
 54+ {"kb", NULL, 1024},
 55+ {"kbyte", "kbytes", 1024},
 56+ {"kilobyte", "kilebytes", 1024},
 57+ {"mb", NULL, 1024 * 1024},
 58+ {"mbyte", "mbytes", 1024 * 1024},
 59+ {"megabyte", "megabytes", 1024 * 1024},
 60+ {NULL, NULL, 0},
 61+};
 62+
 63+static time_t
 64+conf_find_time(name)
 65+ const char *name;
 66+{
 67+ int i;
 68+
 69+ for (i = 0; conf_times[i].name; ++i) {
 70+ if (!strcasecmp(conf_times[i].name, name) ||
 71+ (conf_times[i].plural && !strcasecmp(conf_times[i].plural, name)))
 72+ return conf_times[i].val;
 73+ }
 74+
 75+ return 0;
 76+}
 77+
 78+static struct {
 79+const char *word;
 80+ int yesno;
 81+} yesno[] = {
 82+ {"yes", 1},
 83+ {"no", 0},
 84+ {"true", 1},
 85+ {"false", 0},
 86+ {"on", 1},
 87+ {"off", 0},
 88+ {NULL, 0}
 89+};
 90+
 91+static int
 92+conf_get_yesno_value(str)
 93+ const char *str;
 94+{
 95+ int i;
 96+
 97+ for (i = 0; yesno[i].word; i++)
 98+ if (!strcasecmp(str, yesno[i].word))
 99+ return yesno[i].yesno;
 100+
 101+ return -1;
 102+}
 103+
 104+static void
 105+free_cur_list(list)
 106+ conf_parm_t *list;
 107+{
 108+ switch (list->type & CF_MTYPE) {
 109+ case CF_STRING:
 110+ case CF_QSTRING:
 111+ wfree(list->v.string);
 112+ break;
 113+ case CF_LIST:
 114+ free_cur_list(list->v.list);
 115+ break;
 116+ default: break;
 117+ }
 118+
 119+ if (list->next)
 120+ free_cur_list(list->next);
 121+}
 122+
 123+
 124+conf_parm_t * cur_list = NULL;
 125+
 126+static void
 127+add_cur_list_cpt(new)
 128+ conf_parm_t *new;
 129+{
 130+ if (cur_list == NULL) {
 131+ cur_list = wcalloc(1, sizeof(conf_parm_t));
 132+ cur_list->type |= CF_FLIST;
 133+ cur_list->v.list = new;
 134+ } else {
 135+ new->next = cur_list->v.list;
 136+ cur_list->v.list = new;
 137+ }
 138+}
 139+
 140+static void
 141+add_cur_list(type, str, number)
 142+ int type, number;
 143+ const char *str;
 144+{
 145+ conf_parm_t *new;
 146+
 147+ new = wmalloc(sizeof(conf_parm_t));
 148+ new->next = NULL;
 149+ new->type = type;
 150+
 151+ switch(type) {
 152+ case CF_INT:
 153+ case CF_TIME:
 154+ case CF_YESNO:
 155+ new->v.number = number;
 156+ break;
 157+ case CF_STRING:
 158+ case CF_QSTRING:
 159+ new->v.string = wstrdup(str);
 160+ break;
 161+ }
 162+
 163+ add_cur_list_cpt(new);
 164+}
 165+
 166+
 167+%}
 168+
 169+%union {
 170+ int number;
 171+ char *string;
 172+ conf_parm_t *conf_parm;
 173+}
 174+
 175+%token TWODOTS
 176+
 177+%token <string> QSTRING STRING
 178+%token <number> NUMBER
 179+
 180+%type <string> qstring string
 181+%type <number> number timespec
 182+%type <conf_parm> oneitem single itemlist
 183+
 184+%start conf
 185+
 186+%%
 187+
 188+conf:
 189+ | conf conf_item
 190+ | error
 191+ ;
 192+
 193+conf_item: block
 194+ ;
 195+
 196+block: string
 197+ {
 198+ conf_start_block($1, NULL);
 199+ wfree($1);
 200+ }
 201+ '{' block_items '}' ';'
 202+ {
 203+ if (conf_cur_block)
 204+ conf_end_block(conf_cur_block);
 205+ }
 206+ | string qstring
 207+ {
 208+ conf_start_block($1, $2);
 209+ wfree($1);
 210+ wfree($2);
 211+ }
 212+ '{' block_items '}' ';'
 213+ {
 214+ if (conf_cur_block)
 215+ conf_end_block(conf_cur_block);
 216+ }
 217+ ;
 218+
 219+block_items: block_items block_item
 220+ | block_item
 221+ ;
 222+
 223+block_item: string '=' itemlist ';'
 224+ {
 225+ conf_call_set(conf_cur_block, $1, cur_list, CF_LIST);
 226+ free_cur_list(cur_list);
 227+ cur_list = NULL;
 228+ wfree($1);
 229+ }
 230+ ;
 231+
 232+itemlist: itemlist ',' single
 233+ | single
 234+ ;
 235+
 236+single: oneitem
 237+ {
 238+ add_cur_list_cpt($1);
 239+ }
 240+ | oneitem TWODOTS oneitem
 241+ {
 242+ /* "1 .. 5" meaning 1,2,3,4,5 - only valid for integers */
 243+ if (($1->type & CF_MTYPE) != CF_INT ||
 244+ ($3->type & CF_MTYPE) != CF_INT) {
 245+ conf_report_error("Both arguments in '..' notation must be integers.");
 246+ break;
 247+ } else {
 248+ int i;
 249+
 250+ for (i = $1->v.number; i <= $3->v.number; i++) {
 251+ add_cur_list(CF_INT, 0, i);
 252+ }
 253+ }
 254+ }
 255+ ;
 256+
 257+oneitem: qstring
 258+ {
 259+ $$ = wmalloc(sizeof(conf_parm_t));
 260+ $$->type = CF_QSTRING;
 261+ $$->v.string = wstrdup($1);
 262+ wfree($1);
 263+ }
 264+ | timespec
 265+ {
 266+ $$ = wmalloc(sizeof(conf_parm_t));
 267+ $$->type = CF_TIME;
 268+ $$->v.number = $1;
 269+ }
 270+ | number
 271+ {
 272+ $$ = wmalloc(sizeof(conf_parm_t));
 273+ $$->type = CF_INT;
 274+ $$->v.number = $1;
 275+ }
 276+ | string
 277+ {
 278+ /* a 'string' could also be a yes/no value ..
 279+ so pass it as that, if so */
 280+ int val = conf_get_yesno_value($1);
 281+
 282+ $$ = wmalloc(sizeof(conf_parm_t));
 283+
 284+ if (val != -1) {
 285+ $$->type = CF_YESNO;
 286+ $$->v.number = val;
 287+ } else {
 288+ $$->type = CF_STRING;
 289+ $$->v.string = wstrdup($1);
 290+ }
 291+ wfree($1);
 292+ }
 293+ ;
 294+
 295+qstring: QSTRING { strcpy($$, $1); } ;
 296+string: STRING { strcpy($$, $1); } ;
 297+number: NUMBER { $$ = $1; } ;
 298+
 299+timespec: number string
 300+ {
 301+ time_t t;
 302+
 303+ if ((t = conf_find_time($2)) == 0) {
 304+ conf_report_error("Unrecognised time type/size '%s'", $2);
 305+ t = 1;
 306+ }
 307+
 308+ $$ = $1 * t;
 309+ }
 310+ | timespec timespec
 311+ {
 312+ $$ = $1 + $2;
 313+ }
 314+ | timespec number
 315+ {
 316+ $$ = $1 + $2;
 317+ }
 318+ ;
Index: trunk/willow/src/willow/whttp_entity.c
@@ -0,0 +1,1255 @@
 2+/* @(#) $Header$ */
 3+/* This source code is in the public domain. */
 4+/*
 5+ * Willow: Lightweight HTTP reverse-proxy.
 6+ * whttp_entity: HTTP entity handling.
 7+ */
 8+
 9+#if defined __SUNPRO_C || defined __DECC || defined __HP_cc
 10+# pragma ident "@(#)$Header$"
 11+#endif
 12+
 13+/* How does this work?
 14+ *
 15+ * Each HTTP request can be divided into two entities: the request and the
 16+ * response. The client sends the request, i.e. the headers and possibly
 17+ * a body, to the server, which considers it and sends a reply.
 18+ *
 19+ * Internally, we read the request headers and ignore the
 20+ * body [entity_read_headers]. We then examine the headers
 21+ * [whttp:client_read_done] and decide if it has a body. We modify
 22+ * the entry slightly, and send it to the backend with either no source
 23+ * or, if it had a body, the client's FDE as the source [entity_send].
 24+ * We then wait for the server to reply with its header. When it does
 25+ * [whttp:backend_headers_done], we send the request to the client, using
 26+ * the backend's FDE as the body, if it has one, and close it.
 27+ *
 28+ * See "Entity sending" below for a detailed description of how entity
 29+ * sending works.
 30+ *
 31+ * TODO: We don't have to buffer the headers, _but_ it makes things easier
 32+ * for now and doesn't cost much. if we start not buffering we need to
 33+ * decide what to do when the client goes away unexpectedly. probably it's
 34+ * easiest to just drop the backend connection (this is wasteful of backends
 35+ * but we don't cache them at the moment anyway). what do we do when the
 36+ * client sends "Foo: bar\r\n baz\r\n" and we decide after baz that we
 37+ * shouldn't send that header after all?
 38+ *
 39+ * There is a trade-off in some places between excessive copying and
 40+ * excessive syscalls. In some cases we copy data (headers) when we could
 41+ * undo the parser mangling and send them as-is. IMO this is not likely
 42+ * to be a worthwhile optimisation, needs profiling.
 43+ *
 44+ * As for FDE backending, Unix sucks:
 45+ *
 46+ * The sendfile() function copies data from in_fd to out_fd starting
 47+ * at offset off and of length len bytes. The in_fd argument should
 48+ * be a file descriptor to a regular file opened for reading.
 49+ */
 50+
 51+#include <sys/uio.h>
 52+
 53+#include <unistd.h>
 54+#include <errno.h>
 55+#include <string.h>
 56+#include <stdlib.h>
 57+#include <stdio.h>
 58+#include <assert.h>
 59+/*LINTED*/
 60+#include <fcntl.h>
 61+#include <strings.h>
 62+#include <event.h>
 63+#include <ctype.h>
 64+#include <zlib.h>
 65+
 66+#include "willow.h"
 67+#include "whttp.h"
 68+#include "whttp_entity.h"
 69+#include "wnet.h"
 70+#include "wlog.h"
 71+#include "wconfig.h"
 72+
 73+#define ENTITY_STATE_START 0
 74+#define ENTITY_STATE_HDR 1
 75+#define ENTITY_STATE_DONE 2
 76+#define ENTITY_STATE_SEND_HDR 3
 77+#define ENTITY_STATE_SEND_BODY 4
 78+#define ENTITY_STATE_SEND_BUF 5
 79+
 80+#define ZLIB_BLOCK (1024 * 32)
 81+
 82+static void entity_error_callback(struct bufferevent *, short, void *);
 83+static void entity_read_callback(struct bufferevent *, void *);
 84+static int parse_headers(struct http_entity *);
 85+static int parse_reqtype(struct http_entity *);
 86+static int validhost(const char *);
 87+static int via_includes_me(const char *);
 88+static int write_zlib_eof(struct http_entity *);
 89+static int write_data(struct http_entity *, void *buf, size_t len);
 90+
 91+static void entity_send_headers_done(struct fde *, void *, int);
 92+static void entity_send_fde_write_done(struct fde *, void *, int);
 93+static void entity_send_buf_done(struct fde *, void *, int);
 94+static void entity_send_fde_read(struct fde *);
 95+static void entity_send_file_done(struct fde *, void *, int);
 96+static void entity_send_target_read(struct bufferevent *, void *);
 97+static void entity_send_target_write(struct bufferevent *, void *);
 98+static void entity_send_target_error(struct bufferevent *, short, void *);
 99+
 100+const char *ent_errors[] = {
 101+ /* 0 */ "Unknown error",
 102+ /* -1 */ "Read error",
 103+ /* -2 */ "Could not parse request headers",
 104+ /* -3 */ "Invalid Host",
 105+ /* -4 */ "Invalid request type",
 106+ /* -5 */ "Too many headers",
 107+ /* -6 */ "Forwarding loop detected",
 108+};
 109+
 110+const char *ent_encodings[] = {
 111+ "identity",
 112+ "deflate",
 113+ "x-deflate",
 114+ "gzip",
 115+ "x-gzip",
 116+};
 117+
 118+void
 119+entity_free(entity)
 120+ struct http_entity *entity;
 121+{
 122+ WDEBUG((WLOG_DEBUG, "free entity @ %p", entity));
 123+
 124+ header_free(&entity->he_headers);
 125+ if (entity->_he_frombuf) {
 126+ bufferevent_disable(entity->_he_frombuf, EV_READ | EV_WRITE);
 127+ bufferevent_free(entity->_he_frombuf);
 128+ }
 129+ if (entity->_he_tobuf) {
 130+ bufferevent_disable(entity->_he_tobuf, EV_READ | EV_WRITE);
 131+ bufferevent_free(entity->_he_tobuf);
 132+ }
 133+ if (entity->he_reqstr)
 134+ wfree(entity->he_reqstr);
 135+ if (!entity->he_flags.response) {
 136+ if (entity->he_rdata.request.host)
 137+ wfree(entity->he_rdata.request.host);
 138+ if (entity->he_rdata.request.path)
 139+ wfree(entity->he_rdata.request.path);
 140+ }
 141+ bzero(entity, sizeof(*entity));
 142+}
 143+
 144+void
 145+entity_set_response(ent, isresp)
 146+ struct http_entity *ent;
 147+ int isresp;
 148+{
 149+ if (isresp) {
 150+ if (ent->he_flags.response)
 151+ return;
 152+ if (ent->he_rdata.request.path)
 153+ wfree(ent->he_rdata.request.path);
 154+ if (ent->he_rdata.request.host)
 155+ wfree(ent->he_rdata.request.host);
 156+ bzero(&ent->he_rdata.response, sizeof(ent->he_rdata.response));
 157+ ent->he_flags.response = 1;
 158+ } else {
 159+ if (!ent->he_flags.response)
 160+ return;
 161+ bzero(&ent->he_rdata.request, sizeof(ent->he_rdata.request));
 162+ ent->he_flags.response = 0;
 163+ }
 164+}
 165+
 166+void
 167+entity_read_headers(entity, func, udata)
 168+ struct http_entity *entity;
 169+ header_cb func;
 170+ void *udata;
 171+{
 172+ entity->_he_cbdata = udata;
 173+ entity->_he_func = func;
 174+ entity->he_flags.hdr_only = 1;
 175+
 176+ WDEBUG((WLOG_DEBUG, "entity_read_headers: starting, source %d",
 177+ entity->he_source.fde.fde->fde_fd));
 178+ /* XXX source for an entity header read is _always_ an fde */
 179+ entity->_he_frombuf = bufferevent_new(entity->he_source.fde.fde->fde_fd,
 180+ entity_read_callback, NULL, entity_error_callback, entity);
 181+ bufferevent_disable(entity->_he_frombuf, EV_WRITE);
 182+ bufferevent_enable(entity->_he_frombuf, EV_READ);
 183+// wnet_register(entity->he_source.fde.fde->fde_fd, FDE_READ, entity_read_callback, entity);
 184+ //entity_read_callback(entity->he_source.fde);
 185+}
 186+
 187+void
 188+entity_send(fde, entity, cb, data, flags)
 189+ struct fde *fde;
 190+ struct http_entity *entity;
 191+ header_cb cb;
 192+ void *data;
 193+ int flags;
 194+{
 195+ char status[4];
 196+ int wn_flags = 0;
 197+ char *hdr;
 198+struct header_list *hl;
 199+ int window = 15;
 200+
 201+ errno = 0;
 202+
 203+ entity->_he_func = cb;
 204+ entity->_he_cbdata = data;
 205+ entity->_he_target = fde;
 206+ entity->he_flags.hdr_only = 0;
 207+ if (!entity->he_flags.response && entity->he_rdata.request.reqtype == REQTYPE_POST) {
 208+ entity->he_source.fde._wrt = entity->he_rdata.request.contlen;
 209+ }
 210+
 211+ entity->_he_tobuf = bufferevent_new(entity->_he_target->fde_fd,
 212+ NULL, entity_send_target_write,
 213+ entity_send_target_error, entity);
 214+ bufferevent_disable(entity->_he_tobuf, EV_READ);
 215+ bufferevent_enable(entity->_he_tobuf, EV_WRITE);
 216+ if (entity->_he_frombuf) {
 217+ bufferevent_disable(entity->_he_frombuf, EV_READ);
 218+ bufferevent_disable(entity->_he_frombuf, EV_WRITE);
 219+ }
 220+ entity->_he_state = ENTITY_STATE_SEND_HDR;
 221+
 222+ WDEBUG((WLOG_DEBUG, "entity_send: writing to %d [%s], enc=%d", fde->fde_fd, fde->fde_desc,
 223+ entity->he_encoding));
 224+
 225+ if (entity->he_flags.response) {
 226+ evbuffer_add_printf(entity->_he_tobuf->output, "HTTP/1.1 %d %s\r\n",
 227+ entity->he_rdata.response.status, entity->he_rdata.response.status_str);
 228+ } else {
 229+ evbuffer_add_printf(entity->_he_tobuf->output, "%s %s HTTP/1.1\r\n",
 230+ request_string[entity->he_rdata.request.reqtype],
 231+ entity->he_rdata.request.path);
 232+ }
 233+
 234+ if (flags & ENT_CHUNKED_OKAY) {
 235+ struct header_list *contlen;
 236+ entity->he_flags.chunked = 1;
 237+ if (!entity->he_h_transfer_encoding)
 238+ header_add(&entity->he_headers, wstrdup("Transfer-Encoding"), wstrdup("chunked"));
 239+ if ((contlen = header_find(&entity->he_headers, "Content-Length")) != NULL)
 240+ header_remove(&entity->he_headers, contlen);
 241+ }
 242+
 243+ switch (entity->he_encoding) {
 244+ case E_NONE:
 245+ break;
 246+ case E_GZIP: case E_X_GZIP:
 247+ window += 16;
 248+ case E_DEFLATE: case E_X_DEFLATE: {
 249+ int err;
 250+
 251+ if ((err = deflateInit2(&entity->_he_zbuf, config.complevel, Z_DEFLATED,
 252+ window, 8, Z_DEFAULT_STRATEGY)) != Z_OK) {
 253+ wlog(WLOG_WARNING, "deflateInit: %s", zError(err));
 254+ entity->he_encoding = E_NONE;
 255+ }
 256+ break;
 257+ }
 258+ }
 259+ header_add(&entity->he_headers, wstrdup("Content-Encoding"),
 260+ wstrdup(ent_encodings[entity->he_encoding]));
 261+ for (hl = entity->he_headers.hl_next; hl; hl = hl->hl_next)
 262+ evbuffer_add_printf(entity->_he_tobuf->output, "%s: %s\r\n", hl->hl_name, hl->hl_value);
 263+ bufferevent_write(entity->_he_tobuf, "\r\n", 2);
 264+}
 265+
 266+static void
 267+entity_error_callback(struct bufferevent *be, short what, void *d)
 268+{
 269+struct http_entity *entity = d;
 270+
 271+ /*
 272+ * Some kind of error occured while we were reading from the backend.
 273+ */
 274+ WDEBUG((WLOG_DEBUG, "entity_error_callback called, what=%hd errno=%d %s", what,
 275+ errno, strerror(errno)));
 276+
 277+ if (what & EVBUFFER_EOF) {
 278+ /*
 279+ * End of file from backend.
 280+ */
 281+ if (entity->he_encoding) {
 282+ if (write_zlib_eof(entity) == -1) {
 283+ entity->_he_func(entity, entity->_he_cbdata, -1);
 284+ return;
 285+ }
 286+ }
 287+
 288+ if (entity->he_flags.chunked && !entity->he_flags.eof) {
 289+ WDEBUG((WLOG_DEBUG, "writing chunked data, append EOF"));
 290+ entity->he_flags.eof = 1;
 291+ bufferevent_enable(entity->_he_tobuf, EV_WRITE);
 292+ bufferevent_write(entity->_he_tobuf, "0\r\n", 3);
 293+ return;
 294+ }
 295+ WDEBUG((WLOG_DEBUG, "entity_error_callback: EOF"));
 296+ entity->_he_func(entity, entity->_he_cbdata, 1);
 297+ //entity->he_flags.eof = 1;
 298+ return;
 299+ }
 300+
 301+ entity->he_flags.error = 1;
 302+ entity->_he_func(entity, entity->_he_cbdata, -1);
 303+}
 304+
 305+static int
 306+write_zlib_eof(entity)
 307+ struct http_entity *entity;
 308+{
 309+ int zerr;
 310+ unsigned char zbuf[16384];
 311+ int n;
 312+
 313+ entity->_he_zbuf.avail_in = 0;
 314+ entity->_he_zbuf.next_out = zbuf;
 315+ entity->_he_zbuf.avail_out = sizeof zbuf;
 316+
 317+ zerr = deflate(&entity->_he_zbuf, Z_FINISH);
 318+
 319+ if (zerr != Z_STREAM_END) {
 320+ wlog(WLOG_WARNING, "deflate: %s", zError(zerr));
 321+ deflateEnd(&entity->_he_zbuf);
 322+ return -1;
 323+ }
 324+ n = sizeof zbuf - entity->_he_zbuf.avail_out;
 325+ WDEBUG((WLOG_DEBUG, "writing zlib, append finish, left=%d avail=%d",
 326+ n, entity->_he_zbuf.avail_out));
 327+ if (n) {
 328+ bufferevent_enable(entity->_he_tobuf, EV_WRITE);
 329+ if (entity->he_flags.chunked)
 330+ evbuffer_add_printf(entity->_he_tobuf->output, "%x\r\n", n);
 331+ bufferevent_write(entity->_he_tobuf, zbuf, n);
 332+ if (entity->he_flags.chunked)
 333+ evbuffer_add_printf(entity->_he_tobuf->output, "\r\n");
 334+ }
 335+ deflateEnd(&entity->_he_zbuf);
 336+ return 0;
 337+}
 338+
 339+static void
 340+entity_read_callback(be, d)
 341+ struct bufferevent *be;
 342+ void *d;
 343+{
 344+struct http_entity *entity = d;
 345+ int i;
 346+#define RD_BUFSZ 16386
 347+ char buf[RD_BUFSZ];
 348+ int wrote = 0, contdone = 0;
 349+
 350+ /*
 351+ * Data was available from the backend. If state is ENTITY_STATE_SEND_BODY,
 352+ * we're moving the request from backend->client, so do that. Otherwise,
 353+ * we're still reading header information.
 354+ */
 355+ WDEBUG((WLOG_DEBUG, "entity_read_callback: called, source %d",
 356+ entity->he_source.fde.fde->fde_fd));
 357+
 358+ if (entity->_he_state < ENTITY_STATE_SEND_BODY) {
 359+ if ((i = parse_headers(entity)) < 0) {
 360+ WDEBUG((WLOG_DEBUG, "entity_read_callback: parse_headers returned -1"));
 361+ entity->he_flags.error = 1;
 362+ entity->_he_func(entity, entity->_he_cbdata, i);
 363+ return;
 364+ }
 365+
 366+ WDEBUG((WLOG_DEBUG, "parse_headers returned; client now %d", entity->_he_state));
 367+
 368+ if (entity->_he_state == ENTITY_STATE_DONE) {
 369+ if (entity->he_flags.hdr_only) {
 370+ WDEBUG((WLOG_DEBUG, "entity_read_callback: client is ENTITY_STATE_DONE"));
 371+ bufferevent_disable(entity->_he_frombuf, EV_READ);
 372+ entity->_he_func(entity, entity->_he_cbdata, 0);
 373+ return;
 374+ } else
 375+ entity->_he_state = ENTITY_STATE_SEND_BODY;
 376+ }
 377+ //bufferevent_disable(entity->_he_frombuf, EV_READ);
 378+ //if (entity->he_flags.hdr_only)
 379+ return;
 380+ }
 381+
 382+ if (entity->he_flags.eof) {
 383+ entity->_he_func(entity, entity->_he_cbdata, 0);
 384+ return;
 385+ }
 386+
 387+ bufferevent_enable(entity->_he_tobuf, EV_WRITE);
 388+
 389+ if (entity->he_flags.eof) {
 390+ /*
 391+ * This means the last chunk was written (see below).
 392+ */
 393+ bufferevent_disable(entity->_he_frombuf, EV_READ);
 394+ entity->_he_func(entity, entity->_he_cbdata, 0);
 395+ bufferevent_enable(entity->_he_tobuf, EV_WRITE);
 396+ return;
 397+ }
 398+
 399+ /*
 400+ * While data is available, read it and forward. If we're using chunked encoding,
 401+ * don't read past the end of the chunk.
 402+ */
 403+ for (;;) {
 404+ size_t read;
 405+ size_t want = RD_BUFSZ;
 406+
 407+ /*
 408+ * If we're reading chunked data, check if we're starting a new chunk.
 409+ */
 410+ if ((entity->he_te & TE_CHUNKED) && entity->_he_chunk_size == 0) {
 411+ char *chunks;
 412+ if ((chunks = evbuffer_readline(entity->_he_frombuf->input)) == NULL)
 413+ return;
 414+ entity->_he_chunk_size = strtol(chunks, NULL, 16);
 415+ free(chunks);
 416+ WDEBUG((WLOG_DEBUG, "new chunk, size=%d", entity->_he_chunk_size));
 417+ if (entity->_he_chunk_size == 0) {
 418+ /*
 419+ * Zero-sized chunk = end of request.
 420+ *
 421+ * If this client is receiving TE:chunked data, we have to write
 422+ * the terminating block and finish up the next time round. If
 423+ * not, mark it finished now.
 424+ */
 425+ int more = 0;
 426+
 427+ if (entity->he_encoding) {
 428+ bufferevent_enable(entity->_he_tobuf, EV_WRITE);
 429+ write_zlib_eof(entity);
 430+ more = 1;
 431+ }
 432+
 433+ if (entity->he_flags.chunked) {
 434+ bufferevent_disable(entity->_he_frombuf, EV_READ);
 435+ bufferevent_enable(entity->_he_tobuf, EV_WRITE);
 436+ bufferevent_write(entity->_he_tobuf, "0\r\n", 3);
 437+ more = 1;
 438+ }
 439+
 440+ if (more) {
 441+ entity->he_flags.eof = 1;
 442+ return;
 443+ }
 444+
 445+ bufferevent_disable(entity->_he_frombuf, EV_READ);
 446+ if (!wrote)
 447+ entity->_he_func(entity, entity->_he_cbdata, 0);
 448+ else
 449+ bufferevent_enable(entity->_he_tobuf, EV_WRITE);
 450+ return;
 451+ }
 452+ /* +2 for CRLF */
 453+ entity->_he_chunk_size += 2;
 454+ }
 455+
 456+ want = RD_BUFSZ;
 457+ if (entity->_he_chunk_size)
 458+ want = entity->_he_chunk_size;
 459+ else if (entity->he_source.fde._wrt)
 460+ want = entity->_he_chunk_size;
 461+
 462+ want = entity->_he_chunk_size ? entity->_he_chunk_size : RD_BUFSZ;
 463+
 464+ read = bufferevent_read(entity->_he_frombuf, buf, want);
 465+ WDEBUG((WLOG_DEBUG, "rw %d, got %d wrote=%d wrt=%d", want, read, wrote,
 466+ entity->he_source.fde._wrt));
 467+ if (read == 0) {
 468+ if (!wrote)
 469+ bufferevent_enable(entity->_he_frombuf, EV_READ);
 470+ else
 471+ bufferevent_disable(entity->_he_frombuf, EV_READ);
 472+ return;
 473+ }
 474+
 475+ if (entity->_he_chunk_size)
 476+ entity->_he_chunk_size -= read;
 477+ if (entity->he_source.fde._wrt) {
 478+ entity->he_source.fde._wrt -= read;
 479+ if (entity->he_source.fde._wrt == 0)
 480+ contdone = 1;
 481+ }
 482+
 483+ if ((entity->he_te & TE_CHUNKED) && entity->_he_chunk_size == 0)
 484+ /* subtract the +2 we added above */
 485+ read -= 2;
 486+
 487+ entity->he_size += read;
 488+
 489+ if (entity->he_cache_callback) {
 490+ entity->he_cache_callback(buf, read, entity->he_cache_callback_data);
 491+ }
 492+ bufferevent_enable(entity->_he_tobuf, EV_WRITE);
 493+
 494+ if (write_data(entity, buf, read) == -1) {
 495+ entity->_he_func(entity, entity->_he_cbdata, -1);
 496+ return;
 497+ }
 498+
 499+ wrote++;
 500+ if (contdone) {
 501+ bufferevent_disable(entity->_he_frombuf, EV_READ);
 502+ //entity->_he_func(entity, entity->_he_cbdata, 0);
 503+ entity->he_flags.eof = 1;
 504+ return;
 505+ }
 506+ }
 507+
 508+ bufferevent_disable(entity->_he_frombuf, EV_READ);
 509+}
 510+
 511+static int
 512+write_data(entity, buf, len)
 513+struct http_entity *entity;
 514+ void *buf;
 515+ size_t len;
 516+{
 517+static unsigned char zbuf[ZLIB_BLOCK * 2];
 518+
 519+ switch (entity->he_encoding) {
 520+ case E_NONE:
 521+ if (entity->he_flags.chunked)
 522+ evbuffer_add_printf(entity->_he_tobuf->output, "%x\r\n", len);
 523+ bufferevent_write(entity->_he_tobuf, buf, len);
 524+ if (entity->he_flags.chunked)
 525+ evbuffer_add_printf(entity->_he_tobuf->output, "\r\n");
 526+ return 0;
 527+
 528+ case E_DEFLATE: case E_X_DEFLATE: case E_GZIP: case E_X_GZIP: {
 529+ int zerr;
 530+
 531+ entity->_he_zbuf.next_in = (unsigned char *)buf;
 532+ entity->_he_zbuf.avail_in = len;
 533+ entity->_he_zbuf.next_out = zbuf;
 534+ entity->_he_zbuf.avail_out = sizeof zbuf;
 535+ while (entity->_he_zbuf.avail_in) {
 536+ zerr = deflate(&entity->_he_zbuf, Z_SYNC_FLUSH);
 537+ if (zerr != Z_OK) {
 538+ wlog(WLOG_WARNING, "deflate: %s", zError(zerr));
 539+ return -1;
 540+ }
 541+ WDEBUG((WLOG_DEBUG, "avail_in=%d avail_out=%d",
 542+ entity->_he_zbuf.avail_in, entity->_he_zbuf.avail_out));
 543+ if (entity->he_flags.chunked)
 544+ evbuffer_add_printf(entity->_he_tobuf->output, "%x\r\n",
 545+ (sizeof zbuf - entity->_he_zbuf.avail_out));
 546+ bufferevent_write(entity->_he_tobuf, zbuf,
 547+ (sizeof zbuf - entity->_he_zbuf.avail_out));
 548+ if (entity->he_flags.chunked)
 549+ evbuffer_add_printf(entity->_he_tobuf->output, "\r\n");
 550+ entity->_he_zbuf.next_out = zbuf;
 551+ entity->_he_zbuf.avail_out = sizeof zbuf;
 552+ }
 553+ return 0;
 554+ }
 555+ }
 556+ abort();
 557+}
 558+
 559+static void
 560+entity_send_target_read(struct bufferevent *buf, void *d)
 561+{
 562+ /*
 563+ * Read from target possible. This never happens.
 564+ */
 565+}
 566+
 567+static void
 568+entity_send_target_write(struct bufferevent *buf, void *d)
 569+{
 570+struct http_entity *entity = d;
 571+static char fbuf[ZLIB_BLOCK];
 572+
 573+ WDEBUG((WLOG_DEBUG, "entity_send_target_write: eof=%d, state=%d",
 574+ entity->he_flags.eof, entity->_he_state));
 575+
 576+ if (entity->he_flags.eof) {
 577+ if (entity->_he_frombuf)
 578+ bufferevent_disable(entity->_he_frombuf, EV_READ);
 579+ bufferevent_disable(entity->_he_tobuf, EV_WRITE);
 580+ entity->_he_func(entity, entity->_he_cbdata, 0);
 581+ return;
 582+ }
 583+
 584+ /*
 585+ * Write to target completed.
 586+ */
 587+ if (entity->_he_state == ENTITY_STATE_SEND_HDR) {
 588+ /*
 589+ * Sending headers completed. Decide what to do next.
 590+ */
 591+ switch (entity->he_source_type) {
 592+ case ENT_SOURCE_NONE:
 593+ /* no body for this request */
 594+ WDEBUG((WLOG_DEBUG, "entity_send_target_write: no body, return immediately"));
 595+ entity->_he_func(entity, entity->_he_cbdata, 0);
 596+ return;
 597+
 598+ case ENT_SOURCE_BUFFER:
 599+ /* write buffer, callback when done */
 600+ WDEBUG((WLOG_DEBUG, "entity_send_target_write: source is buffer, %d bytes",
 601+ entity->he_source.buffer.len));
 602+ entity->_he_state = ENTITY_STATE_SEND_BUF;
 603+ bufferevent_write(entity->_he_tobuf,
 604+ (void *)entity->he_source.buffer.addr,
 605+ entity->he_source.buffer.len);
 606+ return;
 607+
 608+ case ENT_SOURCE_FILE:
 609+ /* write file */
 610+ //entity->_he_tobuf = entity->_he_frombuf = NULL;
 611+
 612+ /*
 613+ * Compressed data can't be written using sendfile
 614+ */
 615+ entity->_he_state = ENTITY_STATE_SEND_BODY;
 616+
 617+ if (entity->he_encoding) {
 618+ bufferevent_enable(entity->_he_tobuf, EV_WRITE);
 619+ /* The write handler does this for us */
 620+ return;
 621+ }
 622+
 623+ bufferevent_disable(entity->_he_tobuf, EV_WRITE);
 624+
 625+ if (wnet_sendfile(entity->_he_target->fde_fd, entity->he_source.fd.fd,
 626+ entity->he_source.fd.size - entity->he_source.fd.off,
 627+ entity->he_source.fd.off, entity_send_file_done, entity, 0) == -1) {
 628+ entity->_he_func(entity, entity->_he_cbdata, -1);
 629+ }
 630+ return;
 631+ }
 632+ entity->_he_state = ENTITY_STATE_SEND_BODY;
 633+ }
 634+
 635+ if (entity->_he_state == ENTITY_STATE_SEND_BUF) {
 636+ /*
 637+ * Writing buffer completed.
 638+ */
 639+ bufferevent_disable(entity->_he_tobuf, EV_WRITE);
 640+ bufferevent_disable(entity->_he_frombuf, EV_READ);
 641+ entity->_he_func(entity, entity->_he_cbdata, 0);
 642+ return;
 643+ }
 644+
 645+ if (entity->he_source_type == ENT_SOURCE_FILE) {
 646+ /*
 647+ * We only get here if we're writing deflate data.
 648+ */
 649+ ssize_t i;
 650+
 651+ WDEBUG((WLOG_DEBUG, "write file, eof=%d, size=%d, off=%d",
 652+ entity->he_flags.eof, entity->he_source.fd.size,
 653+ entity->he_source.fd.off));
 654+
 655+ if (entity->he_flags.eof) {
 656+ bufferevent_disable(entity->_he_tobuf, EV_WRITE);
 657+ entity->_he_func(entity, entity->_he_cbdata, 0);
 658+ return;
 659+ }
 660+
 661+ if ((i = read(entity->he_source.fd.fd, fbuf, sizeof fbuf)) == -1) {
 662+ bufferevent_disable(entity->_he_tobuf, EV_WRITE);
 663+ entity->_he_func(entity, entity->_he_cbdata, -1);
 664+ return;
 665+ }
 666+ if (write_data(entity, fbuf, i) == -1) {
 667+ bufferevent_disable(entity->_he_tobuf, EV_WRITE);
 668+ entity->_he_func(entity, entity->_he_cbdata, -1);
 669+ return;
 670+ }
 671+ entity->he_source.fd.off += i;
 672+ if (entity->he_source.fd.off == entity->he_source.fd.size) {
 673+ entity->he_flags.eof = 1;
 674+ write_zlib_eof(entity);
 675+ return;
 676+ }
 677+
 678+ return;
 679+ }
 680+
 681+ WDEBUG((WLOG_DEBUG, "entity_send_target_write: FDE"));
 682+
 683+ /*
 684+ * Otherwise, we're sending from an FDE, and the last write completed.
 685+ */
 686+ bufferevent_enable(entity->_he_frombuf, EV_READ);
 687+ bufferevent_disable(entity->_he_tobuf, EV_WRITE);
 688+ if (!entity->he_flags.drained) {
 689+ entity->he_flags.drained = 1;
 690+ entity_read_callback(entity->_he_frombuf, entity);
 691+ }
 692+}
 693+
 694+static void
 695+entity_send_target_error(struct bufferevent *buf, short err, void *d)
 696+{
 697+struct http_entity *entity = d;
 698+
 699+ /*
 700+ * Writing to target produced an error.
 701+ */
 702+ entity->_he_func(entity, entity->_he_cbdata, -1);
 703+}
 704+
 705+/*ARGSUSED*/
 706+static void
 707+entity_send_file_done(fde, data, res)
 708+ struct fde *fde;
 709+ void *data;
 710+ int res;
 711+{
 712+struct http_entity *entity = data;
 713+
 714+ WDEBUG((WLOG_DEBUG, "entity_send_file_done: called for %d [%s], res=%d",
 715+ fde->fde_fd, fde->fde_desc, res));
 716+ entity->_he_func(entity, entity->_he_cbdata, res);
 717+ return;
 718+}
 719+
 720+static int
 721+validhost(host)
 722+ const char *host;
 723+{
 724+ for (; *host; ++host) {
 725+ if (!(char_table[(unsigned char)*host] & CHAR_HOST))
 726+ return 0;
 727+ }
 728+ return 1;
 729+}
 730+
 731+static int
 732+via_includes_me(s)
 733+ const char *s;
 734+{
 735+ char *orig = wstrdup(s);
 736+ char *via = orig, *comma, *space;
 737+
 738+ do {
 739+ comma = strchr(via, ',');
 740+ if (comma)
 741+ *comma++ = '\0';
 742+ via = strchr(via, ' ');
 743+ if (!via)
 744+ break;
 745+ while (*via == ' ')
 746+ ++via;
 747+ space = strchr(via, ' ');
 748+ if (!space) {
 749+ wfree(orig);
 750+ return 0;
 751+ }
 752+ *space = '\0';
 753+ if (!strcmp(via, my_hostname)) {
 754+ wfree(orig);
 755+ return 1;
 756+ }
 757+ via = comma;
 758+ } while (comma);
 759+ wfree(orig);
 760+ return 0;
 761+}
 762+
 763+#ifdef __lint
 764+# pragma error_messages(off, E_GLOBAL_COULD_BE_STATIC)
 765+#endif
 766+/*
 767+ * Header handling.
 768+ */
 769+void
 770+header_free(head)
 771+ struct header_list *head;
 772+{
 773+struct header_list *next = head->hl_next;
 774+
 775+ while (next) {
 776+ struct header_list *this = next;
 777+ next = this->hl_next;
 778+ wfree((char *)this->hl_name);
 779+ wfree((char *)this->hl_value);
 780+ wfree(this);
 781+ }
 782+
 783+ bzero(head, sizeof(*head));
 784+}
 785+
 786+#ifdef __lint
 787+# pragma error_messages(default, E_GLOBAL_COULD_BE_STATIC)
 788+#endif
 789+
 790+void
 791+header_add(head, name, value)
 792+ struct header_list *head;
 793+ char *name, *value;
 794+{
 795+struct header_list *new = head;
 796+
 797+ head->hl_num++;
 798+
 799+ if (head->hl_tail)
 800+ new = head->hl_tail;
 801+ else
 802+ while (new->hl_next)
 803+ new = new->hl_next;
 804+ new->hl_next = wmalloc(sizeof(*head->hl_next));
 805+ head->hl_tail = new->hl_next;
 806+ head->hl_len += strlen(name) + strlen(value) + 4;
 807+ new = new->hl_next;
 808+ new->hl_name = name;
 809+ new->hl_value = value;
 810+ new->hl_next = new->hl_tail = NULL;
 811+ new->hl_flags = 0;
 812+}
 813+
 814+void
 815+header_append_last(head, append)
 816+ struct header_list *head;
 817+ const char *append;
 818+{
 819+struct header_list *last = head;
 820+ char *cur;
 821+
 822+ assert(last->hl_next);
 823+
 824+ while (last->hl_next)
 825+ last = last->hl_next;
 826+
 827+ cur = last->hl_value;
 828+ last->hl_value = wmalloc(strlen(cur) + 1 + strlen(append) + 1);
 829+ sprintf(last->hl_value, "%s %s", cur, append);
 830+ wfree(cur);
 831+}
 832+
 833+
 834+void
 835+header_remove(head, it)
 836+ struct header_list *head, *it;
 837+{
 838+struct header_list *jt;
 839+
 840+ jt = head;
 841+ while (jt->hl_next && jt->hl_next != it)
 842+ jt = jt->hl_next;
 843+ jt->hl_next = jt->hl_next->hl_next;
 844+ if (it == head->hl_tail)
 845+ head->hl_tail = jt;
 846+ wfree(it->hl_name);
 847+ wfree(it->hl_value);
 848+ wfree(it);
 849+}
 850+
 851+struct header_list *
 852+header_find(head, name)
 853+ struct header_list *head;
 854+ const char *name;
 855+{
 856+struct header_list *it;
 857+
 858+ for (it = head->hl_next; it; it = it->hl_next)
 859+ if (!strcasecmp(name, it->hl_name))
 860+ return it;
 861+ return NULL;
 862+}
 863+
 864+#ifdef __lint
 865+# pragma error_messages(off, E_GLOBAL_COULD_BE_STATIC)
 866+#endif
 867+char *
 868+header_build(head)
 869+ struct header_list *head;
 870+{
 871+ char *buf;
 872+ size_t bufsz;
 873+ size_t buflen = 0;
 874+
 875+ bufsz = head->hl_len + 3;
 876+ if ((buf = wmalloc(bufsz)) == NULL)
 877+ outofmemory();
 878+
 879+ *buf = '\0';
 880+ while (head->hl_next) {
 881+ head = head->hl_next;
 882+ buflen += snprintf(buf + buflen, bufsz - buflen - 1, "%s: %s\r\n", head->hl_name, head->hl_value);
 883+ }
 884+ if (strlcat(buf, "\r\n", bufsz) >= bufsz)
 885+ abort();
 886+
 887+ return buf;
 888+}
 889+#ifdef __lint
 890+# pragma error_messages(default, E_GLOBAL_COULD_BE_STATIC)
 891+#endif
 892+
 893+void
 894+header_dump(head, fd)
 895+ struct header_list *head;
 896+ int fd;
 897+{
 898+ int i = 0;
 899+struct header_list *h;
 900+
 901+ h = head->hl_next;
 902+ while (h) {
 903+ h = h->hl_next;
 904+ ++i;
 905+ }
 906+
 907+ write(fd, &i, sizeof(i));
 908+
 909+ while (head->hl_next) {
 910+ int i, j;
 911+ head = head->hl_next;
 912+ i = strlen(head->hl_name);
 913+ write(fd, &i, sizeof(i));
 914+ j = strlen(head->hl_value);
 915+ write(fd, &j, sizeof(j));
 916+ write(fd, head->hl_name, i);
 917+ write(fd, head->hl_value, j);
 918+ }
 919+}
 920+
 921+int
 922+header_undump(head, fd, len)
 923+ struct header_list *head;
 924+ int fd;
 925+ off_t *len;
 926+{
 927+ int i = 0, j = 0, n = 0;
 928+struct header_list *it = head;
 929+ ssize_t r;
 930+
 931+ *len = 0;
 932+ bzero(head, sizeof(*head));
 933+ if ((r = read(fd, &n, sizeof(n))) < 0) {
 934+ wlog(WLOG_WARNING, "reading cache file: %s", strerror(errno));
 935+ return -1; /* XXX */
 936+ }
 937+
 938+ *len += r;
 939+ WDEBUG((WLOG_DEBUG, "header_undump: %d entries", n));
 940+
 941+ while (n--) {
 942+ char *n, *v, *s;
 943+ int k;
 944+
 945+ if ((it->hl_next = wcalloc(1, sizeof(struct header_list))) == NULL)
 946+ outofmemory();
 947+ it = it->hl_next;
 948+ *len += read(fd, &i, sizeof(i));
 949+ *len += read(fd, &j, sizeof(j));
 950+ WDEBUG((WLOG_DEBUG, "header_undump: i=%d j=%d", i, j));
 951+ n = wmalloc(i + j + 2);
 952+ i = read(fd, n, i);
 953+ *len += i;
 954+ s = n + i;
 955+ *s++ = '\0';
 956+ v = s;
 957+ k = read(fd, s, j);
 958+ *len += k;
 959+ s += k;
 960+ *s = '\0';
 961+ it->hl_name = n;
 962+ it->hl_value = wstrdup(v);
 963+ head->hl_len += i + j + 4;
 964+ }
 965+
 966+ head->hl_tail = it;
 967+ return 0;
 968+}
 969+
 970+static int
 971+parse_headers(entity)
 972+ struct http_entity *entity;
 973+{
 974+ char *line;
 975+
 976+ while ((line = evbuffer_readline(entity->_he_frombuf->input)) != NULL) {
 977+ char **hdr = NULL;
 978+ char *value = NULL;
 979+ int error = 1;
 980+
 981+ if (!line)
 982+ return 0;
 983+
 984+ if (!*line) {
 985+ if (!entity->he_reqstr) {
 986+ free(line);
 987+ return ENT_ERR_INVREQ;
 988+ }
 989+
 990+ entity->_he_state = ENTITY_STATE_DONE;
 991+ free(line);
 992+ return 0;
 993+ }
 994+
 995+ switch(entity->_he_state) {
 996+ case ENTITY_STATE_START:
 997+ entity->he_reqstr = wstrdup(line);
 998+ if (parse_reqtype(entity) == -1) {
 999+ free(line);
 1000+ return ENT_ERR_INVREQ;
 1001+ }
 1002+ entity->_he_state = ENTITY_STATE_HDR;
 1003+ break;
 1004+ case ENTITY_STATE_HDR:
 1005+ if (isspace(*line)) {
 1006+ char *s = line;
 1007+ if (!entity->he_headers.hl_next) {
 1008+ error = -1;
 1009+ goto error;
 1010+ }
 1011+ while (isspace(*s))
 1012+ s++;
 1013+ header_append_last(&entity->he_headers, s);
 1014+ free(line);
 1015+ continue;
 1016+ }
 1017+
 1018+ hdr = wstrvec(line, ":", 2);
 1019+
 1020+ if (!hdr[0] || !hdr[1]) {
 1021+ error = ENT_ERR_INVREQ;
 1022+ goto error;
 1023+ }
 1024+
 1025+ value = hdr[1];
 1026+ while (isspace(*value))
 1027+ ++value;
 1028+ value = wstrdup(value);
 1029+
 1030+ WDEBUG((WLOG_DEBUG, "header: from [%s], [%s] = [%s]",
 1031+ line, hdr[0], value));
 1032+
 1033+ if (++entity->he_headers.hl_num > MAX_HEADERS) {
 1034+ error = ENT_ERR_2MANY;
 1035+ goto error;
 1036+ }
 1037+ if (!strcasecmp(hdr[0], "Host")) {
 1038+ if (!validhost(value)) {
 1039+ error = ENT_ERR_INVHOST;
 1040+ goto error;
 1041+ }
 1042+ header_add(&entity->he_headers, wstrdup(hdr[0]), value);
 1043+ entity->he_rdata.request.host = wstrdup(value);
 1044+ } else if (!strcasecmp(hdr[0], "Content-Length")) {
 1045+ header_add(&entity->he_headers, wstrdup(hdr[0]), value);
 1046+ entity->he_rdata.request.contlen = atoi(value);
 1047+ } else if (!strcasecmp(hdr[0], "Via")) {
 1048+ if (via_includes_me(value)) {
 1049+ error = ENT_ERR_LOOP;
 1050+ goto error;
 1051+ }
 1052+ header_add(&entity->he_headers, wstrdup(hdr[0]), value);
 1053+ } else if (!strcasecmp(hdr[0], "transfer-encoding")) {
 1054+ /* XXX */
 1055+ if (!strcasecmp(value, "chunked")) {
 1056+ entity->he_te |= TE_CHUNKED;
 1057+ }
 1058+ /* Don't forward transfer-encoding... */
 1059+ wfree(value);
 1060+ } else if (!strcasecmp(hdr[0], "Accept-Encoding")) {
 1061+ if (!entity->he_flags.response &&
 1062+ qvalue_parse(&entity->he_rdata.request.accept_encoding, value) == -1) {
 1063+ error = -1;
 1064+ WDEBUG((WLOG_DEBUG, "a-e parse failed"));
 1065+ goto error;
 1066+ }
 1067+ } else if (!strcasecmp(hdr[0], "Pragma")) {
 1068+ entity->he_h_pragma = wstrdup(value);
 1069+ header_add(&entity->he_headers, wstrdup(hdr[0]), entity->he_h_pragma);
 1070+ } else if (!strcasecmp(hdr[0], "Cache-Control")) {
 1071+ entity->he_h_cache_control = wstrdup(value);
 1072+ header_add(&entity->he_headers, wstrdup(hdr[0]), entity->he_h_cache_control);
 1073+ } else if (!strcasecmp(hdr[0], "If-Modified-Since")) {
 1074+ entity->he_h_if_modified_since = wstrdup(value);
 1075+ header_add(&entity->he_headers, wstrdup(hdr[0]), entity->he_h_if_modified_since);
 1076+ } else if (!strcasecmp(hdr[0], "Transfer-Encoding")) {
 1077+ entity->he_h_transfer_encoding = wstrdup(value);
 1078+ header_add(&entity->he_headers, wstrdup(hdr[0]), entity->he_h_transfer_encoding);
 1079+ } else if (!strcasecmp(hdr[0], "Last-Modified")) {
 1080+ entity->he_h_last_modified = wstrdup(value);
 1081+ header_add(&entity->he_headers, wstrdup(hdr[0]), entity->he_h_last_modified);
 1082+ } else
 1083+ header_add(&entity->he_headers, wstrdup(hdr[0]), value);
 1084+
 1085+ wstrvecfree(hdr);
 1086+ break;
 1087+ error:
 1088+ if (hdr)
 1089+ wstrvecfree(hdr);
 1090+ if (value)
 1091+ wfree(value);
 1092+ free(line);
 1093+ return -1;
 1094+ }
 1095+
 1096+ free(line);
 1097+ }
 1098+ return 0;
 1099+}
 1100+
 1101+static int
 1102+parse_reqtype(entity)
 1103+ struct http_entity *entity;
 1104+{
 1105+ char *p, *s, *t;
 1106+ char *request = entity->he_reqstr;;
 1107+ int i;
 1108+
 1109+ WDEBUG((WLOG_DEBUG, "parse_reqtype: called, response=%d", (int)entity->he_flags.response));
 1110+
 1111+ /*
 1112+ * These probably shouldn't be handled in the same function.
 1113+ */
 1114+ if (entity->he_flags.response) {
 1115+ /*
 1116+ * HTTP/1.0
 1117+ */
 1118+ if ((p = strchr(request, ' ')) == NULL)
 1119+ return -1;
 1120+ *p++ = '\0';
 1121+
 1122+ /* 200 */
 1123+ if ((s = strchr(p, ' ')) == NULL)
 1124+ return -1;
 1125+ *s++ = '\0';
 1126+ entity->he_rdata.response.status = atoi(p);
 1127+
 1128+ /* OK */
 1129+ entity->he_rdata.response.status_str = s;
 1130+
 1131+ WDEBUG((WLOG_DEBUG, "parse_reqtype: \"%s\" \"%d\" \"%s\"",
 1132+ request, entity->he_rdata.response.status,
 1133+ entity->he_rdata.response.status_str));
 1134+ return 0;
 1135+ }
 1136+
 1137+ /* GET */
 1138+ if ((p = strchr(request, ' ')) == NULL)
 1139+ return -1;
 1140+
 1141+ *p++ = '\0';
 1142+
 1143+ for (i = 0; supported_reqtypes[i].name; i++)
 1144+ if (!strcmp(request, supported_reqtypes[i].name))
 1145+ break;
 1146+
 1147+ entity->he_rdata.request.reqtype = supported_reqtypes[i].type;
 1148+ if (entity->he_rdata.request.reqtype == REQTYPE_INVALID)
 1149+ return -1;
 1150+
 1151+ /* /path/to/file */
 1152+ if ((s = strchr(p, ' ')) == NULL)
 1153+ return -1;
 1154+
 1155+ *s++ = '\0';
 1156+ if (*p != '/') {
 1157+ /*
 1158+ * This normally means the request URI was of the form
 1159+ * "http://host.tld/file". Clients don't send this, but
 1160+ * Squid does when it thinks we're a proxy.
 1161+ *
 1162+ * Extract the host and set it now. If there's another host
 1163+ * later, it'll overwrite it, but if the client sends two
 1164+ * different hosts it's probably broken anyway...
 1165+ *
 1166+ * We could handle non-http URIs here, but there's not much
 1167+ * points, the backend will reject it anyway.
 1168+ */
 1169+ if (strncmp(p, "http://", 7))
 1170+ return -1;
 1171+ p += 7;
 1172+ t = strchr(p, '/');
 1173+ if (t == NULL)
 1174+ return -1;
 1175+ entity->he_rdata.request.path = wstrdup(t);
 1176+ *t = '\0';
 1177+ entity->he_rdata.request.host = p;
 1178+ } else {
 1179+ entity->he_rdata.request.path = wstrdup(p);
 1180+ }
 1181+
 1182+ /* HTTP/1.0 */
 1183+ if (sscanf(s, "HTTP/%d.%d", &entity->he_rdata.request.httpmaj,
 1184+ &entity->he_rdata.request.httpmin) != 2)
 1185+ return -1;
 1186+
 1187+ return 0;
 1188+}
 1189+
 1190+int
 1191+qvalue_parse(list, header)
 1192+ struct qvalue_head *list;
 1193+ const char *header;
 1194+{
 1195+ char **values;
 1196+ char **value;
 1197+
 1198+ TAILQ_INIT(list);
 1199+
 1200+ values = wstrvec(header, ",", 0);
 1201+ for (value = values; *value; ++value) {
 1202+ char **bits;
 1203+ struct qvalue *entry;
 1204+ bits = wstrvec(*value, ";", 0);
 1205+ entry = wcalloc(1, sizeof(*entry));
 1206+ entry->name = wstrdup(bits[0]);
 1207+ if (bits[1])
 1208+ entry->val = atof(bits[1]);
 1209+ else
 1210+ entry->val = 1.0;
 1211+ TAILQ_INSERT_TAIL(list, entry, entries);
 1212+ wstrvecfree(bits);
 1213+ }
 1214+
 1215+ wstrvecfree(values);
 1216+ return 0;
 1217+}
 1218+
 1219+struct qvalue *
 1220+qvalue_remove_best(list)
 1221+ struct qvalue_head *list;
 1222+{
 1223+ struct qvalue *entry, *best = NULL;
 1224+ float bestf = 0;
 1225+ TAILQ_FOREACH(entry, list, entries) {
 1226+ if (entry->val > bestf) {
 1227+ best = entry;
 1228+ bestf = entry->val;
 1229+ }
 1230+ }
 1231+ if (!best)
 1232+ return NULL;
 1233+ TAILQ_REMOVE(list, best, entries);
 1234+ return best;
 1235+}
 1236+
 1237+enum encoding
 1238+accept_encoding(enc)
 1239+ const char *enc;
 1240+{
 1241+static struct {
 1242+ const char *name;
 1243+ enum encoding value;
 1244+} encs[] = {
 1245+ { "deflate", E_DEFLATE },
 1246+ { "x-deflate", E_X_DEFLATE },
 1247+ { "identity", E_NONE },
 1248+ { "gzip", E_GZIP },
 1249+ { "x-gzip", E_X_GZIP },
 1250+ { NULL, 0 }
 1251+}, *s;
 1252+ for (s = encs; s->name; s++)
 1253+ if (!strcasecmp(s->name, enc))
 1254+ return s->value;
 1255+ return 0;
 1256+}
Index: trunk/willow/src/willow/daemon.c
@@ -0,0 +1,71 @@
 2+/* $Header$ */
 3+/* $NetBSD: daemon.c,v 1.9 2003/08/07 16:42:46 agc Exp $ */
 4+
 5+/*-
 6+ * Copyright (c) 1990, 1993
 7+ * The Regents of the University of California. All rights reserved.
 8+ *
 9+ * Redistribution and use in source and binary forms, with or without
 10+ * modification, are permitted provided that the following conditions
 11+ * are met:
 12+ * 1. Redistributions of source code must retain the above copyright
 13+ * notice, this list of conditions and the following disclaimer.
 14+ * 2. Redistributions in binary form must reproduce the above copyright
 15+ * notice, this list of conditions and the following disclaimer in the
 16+ * documentation and/or other materials provided with the distribution.
 17+ * 3. Neither the name of the University nor the names of its contributors
 18+ * may be used to endorse or promote products derived from this software
 19+ * without specific prior written permission.
 20+ *
 21+ * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
 22+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
 23+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
 24+ * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
 25+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
 26+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
 27+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
 28+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
 29+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
 30+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
 31+ * SUCH DAMAGE.
 32+ */
 33+
 34+#if defined __SUNPRO_C || defined __DECC || defined __HP_cc
 35+# pragma ident "@(#)$Header$"
 36+# pragma ident "$NetBSD: daemon.c,v 1.9 2003/08/07 16:42:46 agc Exp $"
 37+#endif
 38+
 39+#include <fcntl.h>
 40+#include <stdlib.h>
 41+#include <unistd.h>
 42+
 43+int
 44+daemon(nochdir, noclose)
 45+ int nochdir, noclose;
 46+{
 47+ int fd;
 48+
 49+ switch (fork()) {
 50+ case -1:
 51+ return (-1);
 52+ case 0:
 53+ break;
 54+ default:
 55+ _exit(0);
 56+ }
 57+
 58+ if (setsid() == -1)
 59+ return (-1);
 60+
 61+ if (!nochdir)
 62+ (void)chdir("/");
 63+
 64+ if (!noclose && (fd = open("/dev/null", O_RDWR, 0)) != -1) {
 65+ (void)dup2(fd, STDIN_FILENO);
 66+ (void)dup2(fd, STDOUT_FILENO);
 67+ (void)dup2(fd, STDERR_FILENO);
 68+ if (fd > STDERR_FILENO)
 69+ (void)close(fd);
 70+ }
 71+ return (0);
 72+}
Index: trunk/willow/src/willow/wlogwriter.c
@@ -0,0 +1,83 @@
 2+/* @(#) $Header$ */
 3+/* This source code is in the public domain. */
 4+/*
 5+ * Willow: Lightweight HTTP reverse-proxy.
 6+ * wlogwriter: child process for log writing.
 7+ */
 8+
 9+#if defined __SUNPRO_C || defined __DECC || defined __HP_cc
 10+# pragma ident "@(#)$Header$"
 11+#endif
 12+
 13+#include <stdio.h>
 14+#include <unistd.h>
 15+#include <errno.h>
 16+#include <string.h>
 17+#include <stdlib.h>
 18+#include <signal.h>
 19+
 20+#include "willow.h"
 21+#include "wlogwriter.h"
 22+#include "wconfig.h"
 23+#include "wlog.h"
 24+
 25+static void wlogwriter_run(int);
 26+
 27+void
 28+wlogwriter_start(fd)
 29+ int *fd;
 30+{
 31+ switch (fork()) {
 32+ case -1:
 33+ wlog(WLOG_ERROR, "fork: %s", strerror(errno));
 34+ exit(8);
 35+ /*NOTREACHED*/
 36+ case 0:
 37+ (void)close(fd[0]);
 38+ wlogwriter_run(fd[1]);
 39+ break;
 40+ default:
 41+ (void)close(fd[1]);
 42+ break;
 43+ }
 44+}
 45+
 46+static void
 47+wlogwriter_run(pipe)
 48+ int pipe;
 49+{
 50+ FILE *inf, *outf;
 51+ char *line;
 52+ size_t lnsz;
 53+
 54+ lnsz = 8192;
 55+ line = malloc(lnsz);
 56+
 57+ (void)signal(SIGPIPE, SIG_IGN);
 58+
 59+#ifdef HAVE_SETPROCTITLE
 60+ setproctitle("log writer: %s", config.access_log);
 61+#endif
 62+ wlog(WLOG_NOTICE, "wlogwriter starting (pid %d) for %s", (int)getpid(), config.access_log);
 63+
 64+ if ((inf = fdopen(pipe, "r")) == NULL) {
 65+ perror("wlogwriter: fdopen");
 66+ exit(8);
 67+ }
 68+ /*LINTED unsafe fopen*/
 69+ if ((outf = fopen(config.access_log, "a")) == NULL) {
 70+ perror(config.access_log);
 71+ exit(8);
 72+ }
 73+
 74+ while (fgets(line, lnsz, inf)) {
 75+ if (fputs(line, outf) == EOF || fflush(outf) == EOF) {
 76+ wlog(WLOG_NOTICE, "fatal: writing access log: %s", strerror(errno));
 77+ exit(8);
 78+ }
 79+ }
 80+
 81+ wlog(WLOG_NOTICE, "wlogwriter terminating");
 82+ exit(0);
 83+}
 84+
Index: trunk/willow/src/willow/whttp.c
@@ -0,0 +1,848 @@
 2+/* @(#) $Header$ */
 3+/* This source code is in the public domain. */
 4+/*
 5+ * Willow: Lightweight HTTP reverse-proxy.
 6+ * whttp: HTTP implementation.
 7+ */
 8+
 9+#if defined __SUNPRO_C || defined __DECC || defined __HP_cc
 10+# pragma ident "@(#)$Header$"
 11+#endif
 12+
 13+/*
 14+ * The logic of whttp is explained in whttp_entity.c
 15+ */
 16+
 17+#define _GNU_SOURCE /* glibc strptime */
 18+
 19+#include <sys/types.h>
 20+#include <sys/stat.h>
 21+#include <sys/param.h>
 22+
 23+#include <stdlib.h>
 24+#include <stdio.h>
 25+#include <string.h>
 26+#include <unistd.h>
 27+#include <errno.h>
 28+#include <netdb.h>
 29+#include <fcntl.h>
 30+#include <strings.h>
 31+#include <assert.h>
 32+#include <time.h>
 33+
 34+#include "willow.h"
 35+#include "whttp.h"
 36+#include "wnet.h"
 37+#include "wbackend.h"
 38+#include "wconfig.h"
 39+#include "wlogwriter.h"
 40+#include "whttp_entity.h"
 41+#include "wlog.h"
 42+#include "wcache.h"
 43+
 44+#ifndef MAXHOSTNAMELEN
 45+# define MAXHOSTNAMELEN HOST_NAME_MAX /* SysV / BSD disagreement */
 46+#endif
 47+
 48+/*
 49+ * Error handling.
 50+ */
 51+#define ERR_GENERAL 0
 52+#define ERR_BADREQUEST 1
 53+#define ERR_BADRESPONSE 2
 54+#define ERR_CACHE_IO 3
 55+
 56+static const char *error_files[] = {
 57+ /* ERR_GENERAL */ DATADIR "/errors/ERR_GENERAL",
 58+ /* ERR_BADREQUEST */ DATADIR "/errors/ERR_BADREQUEST",
 59+ /* ERR_BADRESPONSE */ DATADIR "/errors/ERR_BADRESPONSE",
 60+ /* ERR_CACHE_IO */ DATADIR "/errors/ERR_CACHE_IO",
 61+};
 62+
 63+const char *request_string[] = {
 64+ "GET",
 65+ "POST",
 66+ "HEAD",
 67+ "TRACE",
 68+ "OPTIONS",
 69+};
 70+
 71+struct request_type supported_reqtypes[] = {
 72+ { "GET", 3, REQTYPE_GET },
 73+ { "POST", 4, REQTYPE_POST },
 74+ { "HEAD", 4, REQTYPE_HEAD },
 75+ { "TRACE", 5, REQTYPE_TRACE },
 76+ { "OPTIONS", 7, REQTYPE_OPTIONS },
 77+ { NULL, 0, REQTYPE_INVALID }
 78+};
 79+
 80+struct http_client {
 81+struct fde *cl_fde; /* backref to fd */
 82+ int cl_reqtype; /* request type or 0 */
 83+ char *cl_path; /* path they want */
 84+ char *cl_wrtbuf; /* write buf (either to client or be) */
 85+struct backend *cl_backend; /* backend servicing this client */
 86+struct fde *cl_backendfde; /* fde for backend */
 87+struct http_entity cl_entity; /* reply to send back */
 88+
 89+ /* Cache-related data */
 90+ int cl_cfd; /* FD of cache file for writing, or 0 */
 91+struct cache_object *cl_co; /* Cache object */
 92+ struct {
 93+ int f_cached:1;
 94+ int f_closed:1;
 95+ int f_http11:1; /* Client understands HTTP/1.1 */
 96+ } cl_flags;
 97+ size_t cl_dsize; /* Object size */
 98+enum encoding cl_enc;
 99+struct http_client *fe_next; /* freelist */
 100+};
 101+
 102+static struct http_client freelist;
 103+static struct http_client deadlist;
 104+
 105+static void client_close(struct http_client *);
 106+static void proxy_start_backend(struct backend *, struct fde *, void *);
 107+static void client_read_done(struct http_entity *, void *, int);
 108+static void client_response_done(struct http_entity *, void *, int);
 109+static void backend_headers_done(struct http_entity *, void *, int);
 110+static void client_headers_done(struct http_entity *, void *, int);
 111+static void client_write_cached(struct http_client *);
 112+static int removable_header(const char *);
 113+
 114+static void client_send_error(struct http_client *, int errcode, const char *error,
 115+ int status, const char *statusstr);
 116+static void client_log_request(struct http_client *);
 117+
 118+static void do_cache_write(const char *, size_t, void *);
 119+
 120+static char via_hdr[1024];
 121+static char *cache_hit_hdr;
 122+static char *cache_miss_hdr;
 123+
 124+char my_hostname[MAXHOSTNAMELEN + 1];
 125+static char my_version[64];
 126+static int logwr_pipe[2];
 127+static FILE *alf;
 128+
 129+/*
 130+ * Initialize whttp, start loggers.
 131+ */
 132+void
 133+whttp_init(void)
 134+{
 135+ int hsize;
 136+
 137+ if (gethostname(my_hostname, MAXHOSTNAMELEN) < 0) {
 138+ perror("gethostname");
 139+ exit(8);
 140+ }
 141+
 142+ (void)strlcpy(my_version, "Willow/" PACKAGE_VERSION, 64);
 143+ snprintf(via_hdr, sizeof(via_hdr), "1.1 %s (%s)", my_hostname, my_version);
 144+
 145+ hsize = sizeof("MISS from ") + strlen(my_hostname);
 146+ cache_hit_hdr = wmalloc(hsize + 1);
 147+ cache_miss_hdr = wmalloc(hsize + 1);
 148+
 149+ if (cache_hit_hdr == NULL || cache_miss_hdr == NULL)
 150+ outofmemory();
 151+
 152+ snprintf(cache_hit_hdr, hsize, "HIT from %s", my_hostname);
 153+ snprintf(cache_miss_hdr, hsize, "MISS from %s", my_hostname);
 154+
 155+ /*
 156+ * Fork the logwriter.
 157+ */
 158+
 159+ if (config.access_log) {
 160+ if (pipe(logwr_pipe) < 0) {
 161+ perror("pipe");
 162+ exit(8);
 163+ }
 164+ switch (fork()) {
 165+ case -1:
 166+ wlog(WLOG_ERROR, "starting logwriter: %s", strerror(errno));
 167+ exit(8);
 168+ /*NOTREACHED*/
 169+ case 0:
 170+ (void)dup2(logwr_pipe[0], STDIN_FILENO);
 171+ /*LINTED execl*/
 172+ (void)execl(LIBEXECDIR "/wlogwriter", "wlogwriter", config.access_log, NULL);
 173+ exit(8);
 174+ /*NOTREACHED*/
 175+ default:
 176+ break;
 177+ }
 178+ if ((alf = fdopen(logwr_pipe[1], "a")) == NULL) {
 179+ perror("whttp: fdopen");
 180+ exit(8);
 181+ }
 182+ }
 183+}
 184+
 185+void
 186+whttp_shutdown(void)
 187+{
 188+ wfree(cache_hit_hdr);
 189+ wfree(cache_miss_hdr);
 190+}
 191+
 192+/*
 193+ * Create a new client associated with the FDE 'e'.
 194+ */
 195+static struct http_client *
 196+new_client(e)
 197+ struct fde *e;
 198+{
 199+struct http_client *cl;
 200+
 201+ if (freelist.fe_next) {
 202+ cl = freelist.fe_next;
 203+ freelist.fe_next = cl->fe_next;
 204+ bzero(cl, sizeof(*cl));
 205+ } else {
 206+ if ((cl = wcalloc(1, sizeof(*cl))) == NULL)
 207+ outofmemory();
 208+ }
 209+
 210+ cl->cl_fde = e;
 211+ return cl;
 212+}
 213+
 214+/*
 215+ * Called by wnet_accept to regiister a new client. Reads the request headers
 216+ * from the client.
 217+ */
 218+void
 219+http_new(e)
 220+ struct fde *e;
 221+{
 222+struct http_client *cl;
 223+
 224+ cl = new_client(e);
 225+ cl->cl_entity.he_source_type = ENT_SOURCE_FDE;
 226+ cl->cl_entity.he_source.fde.fde = e;
 227+ cl->cl_entity.he_rdata.request.contlen = -1;
 228+
 229+ WDEBUG((WLOG_DEBUG, "http_new: starting header read for %d", cl->cl_fde->fde_fd));
 230+ entity_read_headers(&cl->cl_entity, client_read_done, cl);
 231+}
 232+
 233+/*
 234+ * Called when the initial request has been read. Checks if the object is
 235+ * cached, and starts a backend request if not. If it it, sends the cached
 236+ * object to the client.
 237+ */
 238+/*ARGSUSED*/
 239+static void
 240+client_read_done(entity, data, res)
 241+ struct http_entity *entity;
 242+ void *data;
 243+ int res;
 244+{
 245+struct http_client *client = data;
 246+struct cache_object *cobj;
 247+ char *pragma, *cache_control, *ifmod;
 248+struct qvalue_head *acceptenc;
 249+struct qvalue *val;
 250+ int cacheable = 1;
 251+
 252+ WDEBUG((WLOG_DEBUG, "client_read_done: called, res=%d", res));
 253+
 254+ if (res < -1) {
 255+ client_send_error(client, ERR_BADREQUEST, ent_errors[-res], 400, "Bad request (#10.4.1)");
 256+ return;
 257+ } else if (res == -1) {
 258+ client_close(client);
 259+ return;
 260+ } else if (res == 1) {
 261+ client_close(client);
 262+ return;
 263+ }
 264+
 265+ if (client->cl_entity.he_rdata.request.httpmaj >= 1 &&
 266+ client->cl_entity.he_rdata.request.httpmin >= 1)
 267+ client->cl_flags.f_http11 = 1;
 268+
 269+ if (client->cl_entity.he_rdata.request.host == NULL)
 270+ client->cl_path = wstrdup(client->cl_entity.he_rdata.request.path);
 271+ else {
 272+ int len;
 273+
 274+ len = strlen(client->cl_entity.he_rdata.request.host) +
 275+ strlen(client->cl_entity.he_rdata.request.path) + 7;
 276+
 277+ client->cl_path = wmalloc(len + 1);
 278+ if (client->cl_path == NULL)
 279+ outofmemory();
 280+ snprintf(client->cl_path, len + 1, "http://%s%s",
 281+ client->cl_entity.he_rdata.request.host,
 282+ client->cl_entity.he_rdata.request.path);
 283+ }
 284+
 285+ client->cl_reqtype = client->cl_entity.he_rdata.request.reqtype;
 286+
 287+ pragma = entity->he_h_pragma;
 288+ cache_control = entity->he_h_cache_control;
 289+
 290+ if (pragma) {
 291+ char **pragmas = wstrvec(pragma, ",", 0);
 292+ char **s;
 293+ for (s = pragmas; *s; ++s) {
 294+ if (!strcasecmp(*s, "no-cache")) {
 295+ cacheable = 0;
 296+ break;
 297+ }
 298+ }
 299+ wstrvecfree(pragmas);
 300+ }
 301+
 302+ if (cache_control) {
 303+ char **cache_controls = wstrvec(cache_control, ",", 0);
 304+ char **s;
 305+ for (s = cache_controls; *s; ++s) {
 306+ if (!strcasecmp(*s, "no-cache")) {
 307+ cacheable = 0;
 308+ break;
 309+ }
 310+ }
 311+ wstrvecfree(cache_controls);
 312+ }
 313+
 314+ acceptenc = &entity->he_rdata.request.accept_encoding;
 315+ while ((val = qvalue_remove_best(acceptenc)) != NULL) {
 316+ WDEBUG((WLOG_DEBUG, "client offers [%s] q=%f", val->name, (double) val->val));
 317+ if ((client->cl_enc = accept_encoding(val->name)) != E_NONE) {
 318+ wfree(val);
 319+ break;
 320+ }
 321+ wfree(val);
 322+ }
 323+
 324+ /*
 325+ * Check for cached object.
 326+ */
 327+ if (config.ncaches && client->cl_reqtype == REQTYPE_GET) {
 328+ if (cacheable)
 329+ client->cl_co = wcache_find_object(client->cl_path, &client->cl_cfd,
 330+ WCACHE_RDWR);
 331+ else
 332+ client->cl_co = wcache_find_object(client->cl_path, &client->cl_cfd,
 333+ WCACHE_WRONLY);
 334+
 335+ if (cacheable && client->cl_co && client->cl_co->co_time &&
 336+ (ifmod = entity->he_h_if_modified_since)) {
 337+ char *s;
 338+ time_t t;
 339+ struct tm m;
 340+ s = strptime(ifmod, "%a, %d %b %Y %H:%M:%S", &m);
 341+ if (s) {
 342+ t = mktime(&m);
 343+ WDEBUG((WLOG_DEBUG, "if-mod: %d, last-mod: %d", t, client->cl_co->co_time));
 344+ if (t >= client->cl_co->co_time) {
 345+ /*
 346+ * Not modified
 347+ */
 348+ client_send_error(client, -1, NULL, 304, "Not modified (#10.3.5)");
 349+ return;
 350+ }
 351+ }
 352+ }
 353+
 354+ if (client->cl_co && client->cl_co->co_complete) {
 355+ WDEBUG((WLOG_DEBUG, "client_read_done: object %s cached", client->cl_path));
 356+ client_write_cached(client);
 357+ return;
 358+ }
 359+ WDEBUG((WLOG_DEBUG, "client_read_done: %s not cached", client->cl_path));
 360+ }
 361+
 362+ /*
 363+ * Not cached. Find a backend.
 364+ */
 365+ if (get_backend(client->cl_path, proxy_start_backend, client, 0) == -1) {
 366+ client_send_error(client, ERR_GENERAL,
 367+ "No backends were available to service your request", 503,
 368+ "Service unavailable (#10.5.4)");
 369+ return;
 370+ }
 371+}
 372+
 373+/*
 374+ * Called when backend is ready. backend==NULL if none was found.
 375+ */
 376+static void
 377+proxy_start_backend(backend, e, data)
 378+ struct backend *backend;
 379+ struct fde *e;
 380+ void *data;
 381+{
 382+struct http_client *client = data;
 383+struct header_list *it;
 384+ int error = 0;
 385+ socklen_t len = sizeof(error);
 386+
 387+ WDEBUG((WLOG_DEBUG, "proxy_start_backend: called; for client=%d", client->cl_fde->fde_fd));
 388+
 389+ if (backend == NULL) {
 390+ client_send_error(client, ERR_GENERAL,
 391+ "No backends were available to service your request",
 392+ 503, "Service unavailable (#10.5.4)");
 393+ return;
 394+ }
 395+
 396+ client->cl_backend = backend;
 397+ client->cl_backendfde = e;
 398+
 399+ getsockopt(e->fde_fd, SOL_SOCKET, SO_ERROR, &error, &len);
 400+ if (error) {
 401+ client_send_error(client, ERR_GENERAL, strerror(error), 503,
 402+ "Service unavailable (#10.5.4)");
 403+ return;
 404+ }
 405+
 406+ for (it = client->cl_entity.he_headers.hl_next; it; it = it->hl_next) {
 407+ if (removable_header(it->hl_name)) {
 408+ header_remove(&client->cl_entity.he_headers, it);
 409+ it = client->cl_entity.he_headers.hl_next;
 410+ continue;
 411+ }
 412+ }
 413+
 414+ header_add(&client->cl_entity.he_headers, wstrdup("X-Forwarded-For"), wstrdup(client->cl_fde->fde_straddr));
 415+ header_add(&client->cl_entity.he_headers, wstrdup("Connection"), wstrdup("Close"));
 416+ /*
 417+ * POST requests require Content-Length.
 418+ */
 419+ if (client->cl_reqtype == REQTYPE_POST) {
 420+ if (client->cl_entity.he_rdata.request.contlen == -1) {
 421+ client_send_error(client, ERR_BADREQUEST, "POST request without Content-Length",
 422+ 411, "Length required (#10.4.12)");
 423+ return;
 424+ }
 425+
 426+ WDEBUG((WLOG_DEBUG, "client content-length=%d", client->cl_entity.he_rdata.request.contlen));
 427+ client->cl_entity.he_source_type = ENT_SOURCE_FDE;
 428+ client->cl_entity.he_source.fde.fde = client->cl_fde;
 429+ client->cl_entity.he_source.fde.len = client->cl_entity.he_rdata.request.contlen;
 430+ } else
 431+ client->cl_entity.he_source_type = ENT_SOURCE_NONE;
 432+
 433+ entity_send(e, &client->cl_entity, backend_headers_done, client, 0);
 434+}
 435+
 436+/*
 437+ * Called when clients request was written to the backend.
 438+ */
 439+/*ARGSUSED*/
 440+static void
 441+backend_headers_done(entity, data, res)
 442+ struct http_entity *entity;
 443+ void *data;
 444+ int res;
 445+{
 446+struct http_client *client = data;
 447+
 448+ WDEBUG((WLOG_DEBUG, "backend_headers_done: called"));
 449+ if (res == -1) {
 450+ client_send_error(client, ERR_GENERAL, strerror(errno), 503,
 451+ "Service unavailable (#10.5.4)");
 452+ return;
 453+ }
 454+
 455+ entity_free(&client->cl_entity);
 456+ bzero(&client->cl_entity, sizeof(client->cl_entity));
 457+ client->cl_entity.he_source_type = ENT_SOURCE_FDE;
 458+ client->cl_entity.he_source.fde.fde = client->cl_backendfde;
 459+ client->cl_entity.he_source.fde.len = -1;
 460+
 461+ entity_set_response(&client->cl_entity, 1);
 462+
 463+ /*
 464+ * This should probably be handled somewhere inside
 465+ * whttp_entity.c ...
 466+ */
 467+ entity_read_headers(&client->cl_entity, client_headers_done, client);
 468+}
 469+
 470+/*
 471+ * Called when backend's headers are finished reading.
 472+ */
 473+static void
 474+client_headers_done(entity, data, res)
 475+ struct http_entity *entity;
 476+ void *data;
 477+ int res;
 478+{
 479+struct http_client *client = data;
 480+ char *cache_path;
 481+ size_t plen;
 482+
 483+ WDEBUG((WLOG_DEBUG, "client_headers_done: called"));
 484+
 485+ if (res == -1) {
 486+ client_close(client);
 487+ return;
 488+ } else if (res < -1) {
 489+ client_send_error(client, ERR_GENERAL, ent_errors[-res], 503,
 490+ "Service unavailable (#10.5.4)");
 491+ return;
 492+ }
 493+
 494+ /*
 495+ * If cachable, open the cache file and write data.
 496+ *
 497+ * Don't cache responses to non-GET requests, or non-200 replies.
 498+ */
 499+ if (client->cl_reqtype != REQTYPE_GET || entity->he_rdata.response.status != 200
 500+ || !config.ncaches || !client->cl_co) {
 501+ if (client->cl_co)
 502+ wcache_release(client->cl_co, 0);
 503+ } else if (client->cl_co) {
 504+ char *lastmod;
 505+
 506+ entity->he_cache_callback = do_cache_write;
 507+ entity->he_cache_callback_data = client;
 508+ header_dump(&client->cl_entity.he_headers, client->cl_cfd);
 509+
 510+ /*
 511+ * Look for last-modified
 512+ */
 513+ if ((lastmod = entity->he_h_last_modified) != NULL) {
 514+ struct tm tim;
 515+ char *res;
 516+ res = strptime(lastmod, "%a, %d %b %Y %H:%M:%S", &tim);
 517+ if (res) {
 518+ WDEBUG((WLOG_DEBUG, "last-modified: %d", mktime(&tim)));
 519+ client->cl_co->co_time = mktime(&tim);
 520+ }
 521+ }
 522+ }
 523+
 524+ header_add(&client->cl_entity.he_headers, wstrdup("Via"), wstrdup(via_hdr));
 525+ header_add(&client->cl_entity.he_headers, wstrdup("X-Cache"), wstrdup(cache_miss_hdr));
 526+ client->cl_entity.he_source.fde.len = -1;
 527+ if (config.compress)
 528+ client->cl_entity.he_encoding = client->cl_enc;
 529+
 530+ if (!HAS_BODY(client->cl_entity.he_rdata.response.status))
 531+ client->cl_entity.he_source_type = ENT_SOURCE_NONE;
 532+
 533+ entity_send(client->cl_fde, &client->cl_entity, client_response_done, client,
 534+ client->cl_flags.f_http11 ? ENT_CHUNKED_OKAY : 0);
 535+}
 536+
 537+/*
 538+ * Write a cached object to the client.
 539+ */
 540+static void
 541+client_write_cached(client)
 542+ struct http_client *client;
 543+{
 544+ size_t plen;
 545+ char *cache_path;
 546+struct stat sb;
 547+ char size[64];
 548+
 549+ plen = strlen(config.caches[0].dir) + client->cl_co->co_plen + 12 + 2;
 550+ if ((cache_path = wcalloc(1, plen + 1)) == NULL)
 551+ outofmemory();
 552+
 553+ if (fstat(client->cl_cfd, &sb) < 0) {
 554+ wlog(WLOG_WARNING, "stat(%s): %s", cache_path, strerror(errno));
 555+ client_send_error(client, ERR_CACHE_IO, strerror(errno),
 556+ 500, "Internal server error (#10.5.1)");
 557+ wfree(cache_path);
 558+ return;
 559+ }
 560+
 561+ wfree(cache_path);
 562+
 563+ entity_free(&client->cl_entity);
 564+ bzero(&client->cl_entity, sizeof(client->cl_entity));
 565+ header_undump(&client->cl_entity.he_headers, client->cl_cfd, &client->cl_entity.he_source.fd.off);
 566+ header_add(&client->cl_entity.he_headers, wstrdup("Via"), wstrdup(via_hdr));
 567+ header_add(&client->cl_entity.he_headers, wstrdup("X-Cache"), wstrdup(cache_hit_hdr));
 568+ if (!client->cl_enc && !header_find(&client->cl_entity.he_headers, "Content-Length")) {
 569+ snprintf(size, sizeof(size), "%d", client->cl_co->co_size);
 570+ header_add(&client->cl_entity.he_headers, wstrdup("Content-Length"), wstrdup(size));
 571+ }
 572+
 573+ entity_set_response(&client->cl_entity, 1);
 574+ client->cl_entity.he_rdata.response.status = 200;
 575+ client->cl_entity.he_rdata.response.status_str = "OK";
 576+
 577+ client->cl_entity.he_source.fd.fd = client->cl_cfd;
 578+ client->cl_entity.he_source.fd.size = sb.st_size;
 579+ if (config.compress)
 580+ client->cl_entity.he_encoding = client->cl_enc;
 581+
 582+ client->cl_entity.he_source_type = ENT_SOURCE_FILE;
 583+
 584+ client->cl_flags.f_cached = 1;
 585+ entity_send(client->cl_fde, &client->cl_entity, client_response_done, client, 0);
 586+}
 587+
 588+/*
 589+ * Called when response was finished writing to the client.
 590+ */
 591+/*ARGSUSED*/
 592+static void
 593+client_response_done(entity, data, res)
 594+ struct http_entity *entity;
 595+ void *data;
 596+ int res;
 597+{
 598+struct http_client *client = data;
 599+
 600+ WDEBUG((WLOG_DEBUG, "client_response_done: called, res=%d", res));
 601+
 602+ if (client->cl_cfd) {
 603+ if (close(client->cl_cfd) < 0) {
 604+ wlog(WLOG_WARNING, "closing cache file: %s", strerror(errno));
 605+ }
 606+ }
 607+
 608+ if (client->cl_co) {
 609+ int complete = (res != -1);
 610+ struct header_list *hdr;
 611+
 612+ /*
 613+ * The server may have indicated that we shouldn't cache this document.
 614+ * If so, release it in an incomplete state so it gets evicted.
 615+ */
 616+
 617+ /*
 618+ * HTTP/1.0 Pragma
 619+ */
 620+ hdr = header_find(&client->cl_entity.he_headers, "Pragma");
 621+ if (hdr) {
 622+ char **pragmas = wstrvec(hdr->hl_value, ",", 0);
 623+ char **s;
 624+ for (s = pragmas; *s; ++s) {
 625+ if (!strcasecmp(*s, "no-cache")) {
 626+ complete = 0;
 627+ break;
 628+ }
 629+ }
 630+ wstrvecfree(pragmas);
 631+ }
 632+
 633+ /*
 634+ * HTTP/1.1 Cache-Control
 635+ */
 636+ hdr = header_find(&client->cl_entity.he_headers, "Cache-Control");
 637+ if (hdr) {
 638+ char **controls = wstrvec(hdr->hl_value, ",", 0);
 639+ char **s;
 640+ for (s = controls; *s; ++s) {
 641+ /*
 642+ * According to the standard, we can still cache no-cache
 643+ * documents, but we have to revalidate them on every request.
 644+ */
 645+ if (!strcasecmp(*s, "no-cache") ||
 646+ (!config.cache_private && !strcasecmp(*s, "private")) ||
 647+ !strcasecmp(*s, "no-store")) {
 648+ complete = 0;
 649+ break;
 650+ }
 651+ }
 652+ wstrvecfree(controls);
 653+ }
 654+
 655+ wcache_release(client->cl_co, complete);
 656+ }
 657+
 658+ client_log_request(client);
 659+ client_close(client);
 660+}
 661+
 662+static void
 663+client_close(client)
 664+ struct http_client *client;
 665+{
 666+ WDEBUG((WLOG_DEBUG, "close client %d", client->cl_fde->fde_fd));
 667+ assert(!client->cl_flags.f_closed);
 668+ client->cl_flags.f_closed = 1;
 669+ if (client->cl_wrtbuf)
 670+ wfree(client->cl_wrtbuf);
 671+ if (client->cl_path)
 672+ wfree(client->cl_path);
 673+ entity_free(&client->cl_entity);
 674+ wnet_close(client->cl_fde->fde_fd);
 675+ if (client->cl_backendfde)
 676+ wnet_close(client->cl_backendfde->fde_fd);
 677+
 678+ client->fe_next = freelist.fe_next;
 679+ freelist.fe_next = client;
 680+}
 681+
 682+static void
 683+client_send_error(client, errnum, errdata, status, statstr)
 684+ struct http_client *client;
 685+ int errnum, status;
 686+ const char *errdata, *statstr;
 687+{
 688+ FILE *errfile;
 689+ char errbuf[8192];
 690+ char *p = errbuf, *u;
 691+ ssize_t size;
 692+
 693+ if (client->cl_co)
 694+ wcache_release(client->cl_co, 0);
 695+
 696+ if (errnum >= 0) {
 697+ if ((errfile = fopen(error_files[errnum], "r")) == NULL) {
 698+ client_close(client);
 699+ return;
 700+ }
 701+
 702+ if ((size = fread(errbuf, 1, sizeof(errbuf) - 1, errfile)) < 0) {
 703+ (void)fclose(errfile);
 704+ client_close(client);
 705+ return;
 706+ }
 707+
 708+ (void)fclose(errfile);
 709+ errbuf[size] = '\0';
 710+
 711+ if (!errdata)
 712+ errdata = "Unknown error";
 713+ if (!client->cl_path)
 714+ client->cl_path = wstrdup("NONE");
 715+
 716+ u = NULL;
 717+
 718+ while (*p) {
 719+ switch(*p) {
 720+ case '%':
 721+ switch (*++p) {
 722+ case 'U':
 723+ realloc_strcat(&u, client->cl_path);
 724+ break;
 725+ case 'D':
 726+ realloc_strcat(&u, current_time_str);
 727+ break;
 728+ case 'H':
 729+ realloc_strcat(&u, my_hostname);
 730+ break;
 731+ case 'E':
 732+ realloc_strcat(&u, errdata);
 733+ break;
 734+ case 'V':
 735+ realloc_strcat(&u, my_version);
 736+ break;
 737+ case 'C': {
 738+ char *s = wmalloc(4);
 739+ sprintf(s, "%d", status);
 740+ realloc_strcat(&u, s);
 741+ wfree(s);
 742+ break;
 743+ }
 744+ case 'S':
 745+ realloc_strcat(&u, statstr);
 746+ break;
 747+ default:
 748+ break;
 749+ }
 750+ p++;
 751+ continue;
 752+ default:
 753+ realloc_addchar(&u, *p);
 754+ break;
 755+ }
 756+ ++p;
 757+ }
 758+
 759+
 760+ client->cl_wrtbuf = u;
 761+ }
 762+
 763+ header_free(&client->cl_entity.he_headers);
 764+ bzero(&client->cl_entity.he_headers, sizeof(client->cl_entity.he_headers));
 765+ header_add(&client->cl_entity.he_headers, wstrdup("Date"), wstrdup(current_time_str));
 766+ header_add(&client->cl_entity.he_headers, wstrdup("Expires"), wstrdup(current_time_str));
 767+ header_add(&client->cl_entity.he_headers, wstrdup("Server"), wstrdup(my_version));
 768+ header_add(&client->cl_entity.he_headers, wstrdup("Connection"), wstrdup("close"));
 769+
 770+ entity_set_response(&client->cl_entity, 1);
 771+ client->cl_entity.he_rdata.response.status = status;
 772+ client->cl_entity.he_rdata.response.status_str = statstr;
 773+ if (errnum >= 0) {
 774+ header_add(&client->cl_entity.he_headers, wstrdup("Content-Type"), wstrdup("text/html"));
 775+ client->cl_entity.he_source_type = ENT_SOURCE_BUFFER;
 776+ client->cl_entity.he_source.buffer.addr = client->cl_wrtbuf;
 777+ client->cl_entity.he_source.buffer.len = strlen(client->cl_wrtbuf);
 778+ } else {
 779+ client->cl_entity.he_source_type = ENT_SOURCE_NONE;
 780+ }
 781+
 782+ client->cl_entity.he_flags.cachable = 0;
 783+ entity_send(client->cl_fde, &client->cl_entity, client_response_done, client, 0);
 784+}
 785+
 786+static void
 787+client_log_request(client)
 788+ struct http_client *client;
 789+{
 790+ int i;
 791+
 792+ if (!config.access_log)
 793+ return;
 794+
 795+ i = fprintf(alf, "[%s] %s %s \"%s\" %d %s %s\n",
 796+ current_time_short, client->cl_fde->fde_straddr,
 797+ request_string[client->cl_reqtype],
 798+ client->cl_path, client->cl_entity.he_rdata.response.status,
 799+ client->cl_backend ? client->cl_backend->be_name : "-",
 800+ client->cl_flags.f_cached ? "HIT" : "MISS");
 801+ if (i < 0) {
 802+ wlog(WLOG_ERROR, "writing logfile: %s", strerror(errno));
 803+ exit(8);
 804+ }
 805+
 806+ if (fflush(alf) == EOF) {
 807+ wlog(WLOG_ERROR, "flushing logfile: %s", strerror(errno));
 808+ exit(8);
 809+ }
 810+}
 811+
 812+static void
 813+do_cache_write(buf, len, data)
 814+ const char *buf;
 815+ size_t len;
 816+ void *data;
 817+{
 818+struct http_client *client = data;
 819+
 820+ if (write(client->cl_cfd, buf, len) < 0) {
 821+ /*EMPTY*/
 822+ WDEBUG((WLOG_WARNING, "writing cached data: %s", strerror(errno)));
 823+ }
 824+ client->cl_co->co_size += len;
 825+}
 826+
 827+static int
 828+removable_header(hdr)
 829+ const char *hdr;
 830+{
 831+static const char *removable_headers[] = {
 832+ "Connection",
 833+ "Keep-Alive",
 834+ "Proxy-Authenticate",
 835+ "Proxy-Authorization",
 836+ "TE",
 837+ "Trailers",
 838+ "Transfer-Encoding",
 839+ "Upgrade",
 840+ NULL,
 841+};
 842+ const char **s;
 843+ for (s = &removable_headers[0]; *s; s++)
 844+ if (!strcasecmp(*s, hdr))
 845+ return 1;
 846+ return 0;
 847+}
 848+
 849+
Index: trunk/willow/src/willow/lexer.l
@@ -0,0 +1,66 @@
 2+%{
 3+/* @(#) $Header$ */
 4+/* This source code is in the public domain. */
 5+/*
 6+ * Willow: Lightweight HTTP reverse-proxy.
 7+ * lexer: configuration file lexer.
 8+ */
 9+
 10+#if defined __SUNPRO_C || defined __DECC || defined __HP_cc
 11+# pragma ident "@(#)$Header$"
 12+#endif
 13+
 14+#include <string.h>
 15+#include <errno.h>
 16+
 17+#include "willow.h"
 18+#include "confparse.h"
 19+#include "y.tab.h"
 20+
 21+int lineno = 1;
 22+
 23+int
 24+yywrap(void)
 25+{
 26+ return 1;
 27+}
 28+%}
 29+
 30+%x COMMENT
 31+
 32+ws [ \t]*
 33+number [0-9][0-9]*
 34+comment #.*
 35+qstring \"[^\"\n]*[\"]
 36+string [a-zA-Z_\~][a-zA-Z0-9_-]*
 37+
 38+%%
 39+
 40+^[ \t]*"/*" { BEGIN COMMENT; }
 41+^[ \t]*"/*".*"*/"[ \t]*\n { lineno++; }
 42+
 43+<COMMENT>"*/"[ \t]*\n { BEGIN 0; lineno++; break; }
 44+<COMMENT>"*/" { BEGIN 0; break; }
 45+<COMMENT>\n { lineno++; break; }
 46+<COMMENT>.\n { lineno++; break; }
 47+<COMMENT>. { break; }
 48+\n { lineno++; }
 49+{ws} ;
 50+{comment} ;
 51+{number} { char *s;
 52+ yylval.number = strtol(yytext, &s, 0);
 53+ if (*s) {
 54+ conf_report_error("invalid number '%s': %s", yytext, strerror(errno));
 55+ break;
 56+ }
 57+ return NUMBER;
 58+ }
 59+{string} { yylval.string = wstrdup(yytext);
 60+ return STRING;
 61+ }
 62+{qstring} { yylval.string = wstrdup(yytext + 1);
 63+ yylval.string[yyleng - 2] = '\0';
 64+ return QSTRING;
 65+ }
 66+. { return yytext[0];
 67+ }
Index: trunk/willow/src/willow/wcache.c
@@ -0,0 +1,546 @@
 2+/* @(#) $Header$ */
 3+/* This source code is in the public domain. */
 4+/*
 5+ * Willow: Lightweight HTTP reverse-proxy.
 6+ * wcache: entity caching.
 7+ */
 8+
 9+#if defined __SUNPRO_C || defined __DECC || defined __HP_cc
 10+# pragma ident "@(#)$Header$"
 11+#endif
 12+
 13+#include <sys/types.h>
 14+#include <sys/stat.h>
 15+
 16+#include <string.h>
 17+#include <errno.h>
 18+#include <stdlib.h>
 19+#include <math.h>
 20+#include <strings.h>
 21+#include <limits.h>
 22+#include <assert.h>
 23+#include <unistd.h>
 24+#include <fcntl.h>
 25+#include <event.h>
 26+
 27+#include "wcache.h"
 28+#include "wlog.h"
 29+#include "wconfig.h"
 30+#include "willow.h"
 31+
 32+#define CACHEDIR "__objects__"
 33+
 34+static void dberror(const char *, int);
 35+static int cache_next_id(void);
 36+static void run_expiry(int, short, void*);
 37+static void wcache_evict(struct cache_object *);
 38+static void cache_getstate(struct cache_state *);
 39+static struct cache_object *wcache_new_object(const char *);
 40+static void wcache_free_object(struct cache_object *);
 41+static int cache_open(struct cache_object *, int, int);
 42+static void cache_unlink(struct cache_object *);
 43+static void cache_writestate(struct cache_state *state);
 44+static void expire_sched(void);
 45+static void idx_add(struct cache_object *);
 46+static void idx_rem(struct cache_object *);
 47+static struct cache_object *idx_find(const char *key);
 48+
 49+typedef unsigned long int ub4; /* unsigned 4-byte quantities */
 50+typedef unsigned char ub1; /* unsigned 1-byte quantities */
 51+static ub4 hash(const ub1 *);
 52+
 53+#define hashsize(n) ((ub4)1<<(n))
 54+#define hashmask(n) (hashsize(n)-1)
 55+
 56+#define HASH_BITS 20
 57+#define HASH_ELEMS hashsize(HASH_BITS)
 58+
 59+static struct cache_state state;
 60+static struct event expire_ev;
 61+static struct timeval expire_tv;
 62+
 63+static int int_max_len;
 64+
 65+struct key_idx_entry {
 66+ struct cache_object *obj;
 67+ LIST_ENTRY(key_idx_entry) entries;
 68+};
 69+
 70+struct key_idx_bucket {
 71+ LIST_HEAD(key_idx_head, key_idx_entry) head;
 72+} key_idx[HASH_ELEMS];
 73+
 74+TAILQ_HEAD(objlist, cache_object) objects;
 75+
 76+static int
 77+cache_open(obj, flags, mode)
 78+ struct cache_object *obj;
 79+ int flags, mode;
 80+{
 81+ char *path;
 82+ int plen;
 83+ int i;
 84+
 85+ plen = strlen(config.caches[0].dir) + 1 + sizeof(CACHEDIR) + 1 + 6 + int_max_len;
 86+ path = wmalloc(plen + 1);
 87+ sprintf(path, "%s/%s/%s", config.caches[0].dir, CACHEDIR, obj->co_path);
 88+ if (mode)
 89+ unlink(path);
 90+ i = open(path, flags, mode);
 91+ wfree(path);
 92+ return i;
 93+}
 94+
 95+static void
 96+cache_unlink(obj)
 97+ struct cache_object *obj;
 98+{
 99+ char *path;
 100+ int plen;
 101+ int i;
 102+
 103+ plen = strlen(config.caches[0].dir) + 1 + sizeof(CACHEDIR) + 1 + 6 + int_max_len;
 104+ path = wmalloc(plen + 1);
 105+ sprintf(path, "%s/%s/%s", config.caches[0].dir, CACHEDIR, obj->co_path);
 106+ unlink(path);
 107+ wfree(path);
 108+ return;
 109+}
 110+
 111+void
 112+wcache_setupfs(void)
 113+{
 114+ int i, j, k;
 115+struct cachedir *cd;
 116+struct cache_state state;
 117+
 118+ for (cd = config.caches; cd < config.caches + config.ncaches; ++cd) {
 119+ size_t len, dlen;
 120+ char *dir;
 121+
 122+ dlen = strlen(cd->dir) + sizeof(CACHEDIR) + 2 + 6 /* 0/1/2/ */;
 123+ if ((dir = wmalloc(dlen)) == NULL)
 124+ outofmemory();
 125+
 126+ snprintf(dir, dlen, "%s/%s", cd->dir, CACHEDIR);
 127+
 128+ /* create base directory if it doesn't exist */
 129+ /*LINTED unsafe mkdir*/
 130+ if (mkdir(cd->dir, 0700) < 0 || mkdir(dir, 0700) < 0) {
 131+ wlog(WLOG_ERROR, "%s: mkdir: %s", cd->dir, strerror(errno));
 132+ exit(8);
 133+ }
 134+
 135+ for (i = 0; i < 10; ++i) {
 136+ snprintf(dir, dlen, "%s/%s/%d", cd->dir, CACHEDIR, i);
 137+
 138+ /*LINTED unsafe mkdir*/
 139+ if (mkdir(dir, 0700) < 0) {
 140+ wlog(WLOG_ERROR, "%s: mkdir: %s", dir, strerror(errno));
 141+ exit(8);
 142+ }
 143+
 144+ for (j = 0; j < 10; ++j) {
 145+ snprintf(dir, dlen, "%s/%s/%d/%d", cd->dir, CACHEDIR, i, j);
 146+ /*LINTED unsafe mkdir*/
 147+ if (mkdir(dir, 0700) < 0) {
 148+ wlog(WLOG_ERROR, "%s: mkdir: %s", dir, strerror(errno));
 149+ exit(8);
 150+ }
 151+ for (k = 0; k < 10; ++k) {
 152+ snprintf(dir, dlen, "%s/%s/%d/%d/%d", cd->dir, CACHEDIR, i, j, k);
 153+ /*LINTED unsafe mkdir*/
 154+ if (mkdir(dir, 0700) < 0) {
 155+ wlog(WLOG_ERROR, "%s: mkdir: %s", dir, strerror(errno));
 156+ exit(8);
 157+ }
 158+ }
 159+ }
 160+ }
 161+ wfree(dir);
 162+ wlog(WLOG_NOTICE, "created cache directory structure for %s", cd->dir);
 163+ }
 164+ wcache_init(0);
 165+
 166+ bzero(&state, sizeof(state));
 167+ state.cs_id = 1000;
 168+ state.cs_size = 0;
 169+ cache_writestate(&state);
 170+
 171+ wlog(WLOG_NOTICE, "wrote initial cache state");
 172+ wcache_shutdown();
 173+}
 174+
 175+void
 176+wcache_shutdown(void)
 177+{
 178+ cache_writestate(&state);
 179+}
 180+
 181+void
 182+wcache_init(readstate)
 183+ int readstate;
 184+{
 185+struct cachedir *cd;
 186+ int i;
 187+
 188+ if (config.ncaches == 0) {
 189+ wlog(WLOG_WARNING, "no cache directories specified");
 190+ return;
 191+ }
 192+
 193+ /* only one cache dir supported for now... */
 194+ for (cd = config.caches; cd < config.caches + config.ncaches; ++cd) {
 195+ }
 196+
 197+ int_max_len = (int) log10((double) INT_MAX) + 1;
 198+
 199+ TAILQ_INIT(&objects);
 200+
 201+ cache_getstate(&state);
 202+ if (readstate) {
 203+ expire_sched();
 204+ }
 205+}
 206+
 207+static void
 208+expire_sched()
 209+{
 210+ expire_tv.tv_usec = 0;
 211+ expire_tv.tv_sec = config.cache_expevery;
 212+ evtimer_set(&expire_ev, run_expiry, NULL);
 213+ event_add(&expire_ev, &expire_tv);
 214+}
 215+
 216+void
 217+wcache_release(obj, comp)
 218+ struct cache_object *obj;
 219+ int comp;
 220+{
 221+ WDEBUG((WLOG_DEBUG, "release %s, comp=%d", obj->co_key, comp));
 222+
 223+ if (comp) {
 224+ if (!obj->co_complete)
 225+ state.cs_size += obj->co_size;
 226+ obj->co_complete = 1;
 227+ } else if (!obj->co_complete) {
 228+ wcache_evict(obj);
 229+ }
 230+}
 231+
 232+static void
 233+wcache_evict(obj)
 234+ struct cache_object *obj;
 235+{
 236+ TAILQ_REMOVE(&objects, obj, entries);
 237+ cache_unlink(obj);
 238+ state.cs_size -= obj->co_size;
 239+ idx_rem(obj);
 240+ wcache_free_object(obj);
 241+}
 242+
 243+struct cache_object *
 244+wcache_find_object(key, fd, flags)
 245+ const char *key;
 246+ int *fd, flags;
 247+{
 248+struct cache_object *co;
 249+
 250+ WDEBUG((WLOG_DEBUG, "wcache_find_object: looking for %s", key));
 251+ co = idx_find(key);
 252+
 253+ if (co) {
 254+ /*
 255+ * If they only want it for writing, fail if it exists
 256+ */
 257+ if (flags & WCACHE_WRONLY)
 258+ return NULL;
 259+
 260+ if (!strcmp(key, co->co_key)) {
 261+ if (!co->co_complete) {
 262+ return NULL;
 263+ }
 264+ *fd = cache_open(co, O_RDONLY, 0);
 265+ WDEBUG((WLOG_DEBUG, "found! fd=%d", *fd));
 266+ if (*fd == -1) {
 267+ wlog(WLOG_WARNING, "opening cache file %s: %s", co->co_path, strerror(errno));
 268+ co = NULL;
 269+ }
 270+ return co;
 271+ }
 272+ }
 273+
 274+ co = wcache_new_object(key);
 275+ idx_add(co);
 276+ if ((*fd = cache_open(co, O_WRONLY | O_CREAT | O_EXCL, 0600)) == -1) {
 277+ wlog(WLOG_WARNING, "opening cached file: %s", strerror(errno));
 278+ wcache_free_object(co);
 279+ return NULL;
 280+ }
 281+
 282+ return co;
 283+}
 284+
 285+static void
 286+cache_writestate(state)
 287+ struct cache_state *state;
 288+{
 289+ FILE *stfil;
 290+ char *stpath;
 291+ int stlen;
 292+struct cache_object *obj;
 293+
 294+ stlen = strlen(config.caches[0].dir) + 1 + 5 + 1;
 295+ if ((stpath = wmalloc(stlen)) == NULL)
 296+ outofmemory();
 297+ snprintf(stpath, stlen, "%s/%s", config.caches[0].dir, "state");
 298+ if ((stfil = fopen(stpath, "w")) == NULL) {
 299+ wlog(WLOG_WARNING, "opening cache dir %s: %s", stpath, strerror(errno));
 300+ exit(8);
 301+ }
 302+ fprintf(stfil, "%lld %lld\n", state->cs_id, state->cs_size);
 303+ TAILQ_FOREACH(obj, &objects, entries) {
 304+ if (!obj->co_complete)
 305+ continue;
 306+ fprintf(stfil, "%s %s %d %lu %lu %d %lu\n", obj->co_key, obj->co_path, obj->co_size,
 307+ (unsigned long) obj->co_time, (unsigned long) obj->co_lru, obj->co_id,
 308+ (unsigned long) obj->co_expires);
 309+ }
 310+ fclose(stfil);
 311+}
 312+
 313+static void
 314+cache_getstate(state)
 315+ struct cache_state *state;
 316+{
 317+ FILE *stfil;
 318+ char *stpath;
 319+ int stlen;
 320+struct cache_object *obj;
 321+ int i;
 322+ char *s;
 323+ size_t l;
 324+
 325+ stlen = strlen(config.caches[0].dir) + 1 + 5 + 1;
 326+ if ((stpath = wmalloc(stlen)) == NULL)
 327+ outofmemory();
 328+ snprintf(stpath, stlen, "%s/%s", config.caches[0].dir, "state");
 329+ if ((stfil = fopen(stpath, "r")) == NULL) {
 330+ wlog(WLOG_WARNING, "opening cache state %s: %s", stpath, strerror(errno));
 331+ wlog(WLOG_WARNING, "using default cache state");
 332+ state->cs_id = 1000;
 333+ return;
 334+ }
 335+
 336+ if (fscanf(stfil, "%lld %lld\n", &state->cs_id, &state->cs_size) != 2) {
 337+ wlog(WLOG_ERROR, "data format error in cache state file %s", stpath);
 338+ exit(8);
 339+ }
 340+
 341+ s = wmalloc(65535);
 342+ while (fgets(s, 65534, stfil)) {
 343+ char url[65535], path[128];
 344+ int size, time, lru, id, expires;
 345+ struct cache_object *obj;
 346+ if (sscanf(s, "%65534s %127s %d %d %d %d %d", url, path, &size, &time, &lru, &id, &expires) != 7) {
 347+ wlog(WLOG_ERROR, "data format error in cache state file %s", stpath);
 348+ exit(8);
 349+ }
 350+ obj = wmalloc(sizeof(*obj));
 351+ bzero(obj, sizeof(*obj));
 352+ obj->co_key = wstrdup(url);
 353+ obj->co_size = size;
 354+ obj->co_path = wstrdup(path);
 355+ obj->co_complete = 1;
 356+ obj->co_time = time;
 357+ obj->co_lru = lru;
 358+ obj->co_id = id;
 359+ obj->co_expires = expires;
 360+ TAILQ_INSERT_TAIL(&objects, obj, entries);
 361+ idx_add(obj);
 362+ WDEBUG((WLOG_DEBUG, "load %s %s from cache", obj->co_key, obj->co_path));
 363+ }
 364+
 365+}
 366+
 367+static int
 368+cache_next_id(void)
 369+{
 370+ int i;
 371+ i = state.cs_id;
 372+ ++state.cs_id;
 373+ return i;
 374+}
 375+
 376+static struct cache_object *
 377+wcache_new_object(key)
 378+ const char *key;
 379+{
 380+struct cache_object *ret;
 381+ int i;
 382+ char *p, *s, a[11];
 383+
 384+ if ((ret = wcalloc(1, sizeof(*ret))) == NULL) {
 385+ outofmemory();
 386+ /*NOTREACHED*/
 387+ }
 388+
 389+ ret->co_id = cache_next_id();
 390+ ret->co_key = wstrdup(key);
 391+
 392+ assert(ret->co_id > 999);
 393+ ret->co_plen = int_max_len + 6;
 394+ if ((ret->co_path = wmalloc(ret->co_plen + 1)) == NULL) {
 395+ outofmemory();
 396+ /*NOTREACHED*/
 397+ }
 398+ p = ret->co_path;
 399+ snprintf(a, 10, "%d", ret->co_id);
 400+ s = a + strlen(a) - 1;
 401+ WDEBUG((WLOG_DEBUG, "id=%d a=%s", ret->co_id, a));
 402+
 403+ for (i = 0; i < 3; ++i) {
 404+ *p++ = *s--;
 405+ *p++ = '/';
 406+ }
 407+ *p = '\0';
 408+ if (strlcat(ret->co_path, a, ret->co_plen + 1) >= ret->co_plen + 1)
 409+ abort();
 410+ WDEBUG((WLOG_DEBUG, "new object path is [%s], len %d", ret->co_path, ret->co_plen));
 411+
 412+ TAILQ_INSERT_HEAD(&objects, ret, entries);
 413+ return ret;
 414+}
 415+
 416+static void
 417+wcache_free_object(obj)
 418+ struct cache_object *obj;
 419+{
 420+}
 421+
 422+static void
 423+run_expiry(int fd, short ev, void *data)
 424+{
 425+ w_size_t wantsize;
 426+ int i;
 427+struct cache_object *obj;
 428+
 429+ WDEBUG((WLOG_DEBUG, "expire: start, run every %d, cache is %lld bytes large",
 430+ config.cache_expevery, state.cs_size));
 431+
 432+ cache_writestate(&state);
 433+ wantsize = config.caches[0].maxsize * ((100.0-config.cache_expthresh)/100);
 434+ if (state.cs_size <= wantsize) {
 435+ WDEBUG((WLOG_DEBUG, "expire: cache only %lld bytes large", state.cs_size));
 436+ expire_sched();
 437+ return;
 438+ }
 439+ while (state.cs_size > wantsize) {
 440+ struct cache_object *obj = TAILQ_LAST(&objects, objlist);
 441+ WDEBUG((WLOG_DEBUG, "expiring some objects, size=%lld, want=%lld, obj=%p, last=%p, *last=%p",
 442+ state.cs_size, wantsize, obj, objects.tqh_last, *objects.tqh_last));
 443+ if (!obj)
 444+ break;
 445+ wcache_evict(obj);
 446+ }
 447+ expire_sched();
 448+}
 449+
 450+static void
 451+idx_add(obj)
 452+ struct cache_object *obj;
 453+{
 454+ struct key_idx_head *head = &key_idx[hash((ub1 *)obj->co_key)].head;
 455+ struct key_idx_entry *entry = wmalloc(sizeof(*entry));
 456+ bzero(entry, sizeof(*entry));
 457+ entry->obj = obj;
 458+ LIST_INSERT_HEAD(head, entry, entries);
 459+}
 460+
 461+static struct cache_object *
 462+idx_find(key)
 463+ const char *key;
 464+{
 465+ struct key_idx_head *head = &key_idx[hash((ub1 *)key)].head;
 466+ struct key_idx_entry *entry;
 467+ LIST_FOREACH(entry, head, entries)
 468+ if (!strcmp(entry->obj->co_key, key))
 469+ return entry->obj;
 470+ return NULL;
 471+}
 472+
 473+static void
 474+idx_rem(obj)
 475+ struct cache_object *obj;
 476+{
 477+ struct key_idx_head *head = &key_idx[hash((ub1 *)obj->co_key)].head;
 478+ struct key_idx_entry *entry;
 479+ LIST_FOREACH(entry, head, entries)
 480+ if (entry->obj == obj)
 481+ LIST_REMOVE(entry, entries);
 482+}
 483+
 484+#define mix(a,b,c) \
 485+{ \
 486+ a -= b; a -= c; a ^= (c>>13); \
 487+ b -= c; b -= a; b ^= (a<<8); \
 488+ c -= a; c -= b; c ^= (b>>13); \
 489+ a -= b; a -= c; a ^= (c>>12); \
 490+ b -= c; b -= a; b ^= (a<<16); \
 491+ c -= a; c -= b; c ^= (b>>5); \
 492+ a -= b; a -= c; a ^= (c>>3); \
 493+ b -= c; b -= a; b ^= (a<<10); \
 494+ c -= a; c -= b; c ^= (b>>15); \
 495+}
 496+
 497+/*
 498+ * By Bob Jenkins, 1996. bob_jenkins@burtleburtle.net. You may use this
 499+ * code any way you wish, private, educational, or commercial. It's free.
 500+ *
 501+ * See http://burtleburtle.net/bob/hash/evahash.html
 502+ */
 503+
 504+ub4 hash(k)
 505+register const ub1 *k; /* the key */
 506+{
 507+ register ub4 a,b,c,len;
 508+ register ub4 length = strlen((char *)k);
 509+ register ub4 initval = 0;
 510+
 511+ /* Set up the internal state */
 512+ len = length;
 513+ a = b = 0x9e3779b9; /* the golden ratio; an arbitrary value */
 514+ c = initval; /* the previous hash value */
 515+
 516+ /*---------------------------------------- handle most of the key */
 517+ while (len >= 12)
 518+ {
 519+ a += (k[0] +((ub4)k[1]<<8) +((ub4)k[2]<<16) +((ub4)k[3]<<24));
 520+ b += (k[4] +((ub4)k[5]<<8) +((ub4)k[6]<<16) +((ub4)k[7]<<24));
 521+ c += (k[8] +((ub4)k[9]<<8) +((ub4)k[10]<<16)+((ub4)k[11]<<24));
 522+ mix(a,b,c);
 523+ k += 12; len -= 12;
 524+ }
 525+
 526+ /*------------------------------------- handle the last 11 bytes */
 527+ c += length;
 528+ switch(len) /* all the case statements fall through */
 529+ {
 530+ case 11: c+=((ub4)k[10]<<24);
 531+ case 10: c+=((ub4)k[9]<<16);
 532+ case 9 : c+=((ub4)k[8]<<8);
 533+ /* the first byte of c is reserved for the length */
 534+ case 8 : b+=((ub4)k[7]<<24);
 535+ case 7 : b+=((ub4)k[6]<<16);
 536+ case 6 : b+=((ub4)k[5]<<8);
 537+ case 5 : b+=k[4];
 538+ case 4 : a+=((ub4)k[3]<<24);
 539+ case 3 : a+=((ub4)k[2]<<16);
 540+ case 2 : a+=((ub4)k[1]<<8);
 541+ case 1 : a+=k[0];
 542+ /* case 0: nothing left to add */
 543+ }
 544+ mix(a,b,c);
 545+ /*-------------------------------------------- report the result */
 546+ return c & hashmask(HASH_BITS);
 547+}
Index: trunk/willow/src/willow/strlcat.c
@@ -0,0 +1,65 @@
 2+/* @(#)$Header$ */
 3+/* $NetBSD: strlcat.c,v 1.16 2003/10/27 00:12:42 lukem Exp $ */
 4+/* $OpenBSD: strlcat.c,v 1.10 2003/04/12 21:56:39 millert Exp $ */
 5+
 6+/*
 7+ * Copyright (c) 1998 Todd C. Miller <Todd.Miller@courtesan.com>
 8+ *
 9+ * Permission to use, copy, modify, and distribute this software for any
 10+ * purpose with or without fee is hereby granted, provided that the above
 11+ * copyright notice and this permission notice appear in all copies.
 12+ *
 13+ * THE SOFTWARE IS PROVIDED "AS IS" AND TODD C. MILLER DISCLAIMS ALL
 14+ * WARRANTIES WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES
 15+ * OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL TODD C. MILLER BE LIABLE
 16+ * FOR ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
 17+ * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION
 18+ * OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN
 19+ * CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
 20+ */
 21+
 22+#if defined __SUNPRO_C || defined __DECC || defined __HP_cc
 23+# pragma ident "@(#)$Header$"
 24+#endif
 25+
 26+#include <sys/types.h>
 27+#include <assert.h>
 28+#include <string.h>
 29+
 30+/*
 31+ * Appends src to string dst of size siz (unlike strncat, siz is the
 32+ * full size of dst, not space left). At most siz-1 characters
 33+ * will be copied. Always NUL terminates (unless siz <= strlen(dst)).
 34+ * Returns strlen(src) + MIN(siz, strlen(initial dst)).
 35+ * If retval >= siz, truncation occurred.
 36+ */
 37+size_t
 38+strlcat(dst, src, siz)
 39+ char *dst;
 40+ const char *src;
 41+ size_t siz;
 42+{
 43+ char *d = dst;
 44+ const char *s = src;
 45+ size_t n = siz;
 46+ size_t dlen;
 47+
 48+ /* Find the end of dst and adjust bytes left but don't go past end */
 49+ while (n-- != 0 && *d != '\0')
 50+ d++;
 51+ dlen = d - dst;
 52+ n = siz - dlen;
 53+
 54+ if (n == 0)
 55+ return(dlen + strlen(s));
 56+ while (*s != '\0') {
 57+ if (n != 1) {
 58+ *d++ = *s;
 59+ n--;
 60+ }
 61+ s++;
 62+ }
 63+ *d = '\0';
 64+
 65+ return(dlen + (s - src)); /* count does not include NUL */
 66+}
Index: trunk/willow/src/willow/wconfig.c
@@ -0,0 +1,128 @@
 2+/* @(#) $Header$ */
 3+/* This source code is in the public domain. */
 4+/*
 5+ * Willow: Lightweight HTTP reverse-proxy.
 6+ * wconfig: configuration.
 7+ */
 8+
 9+#if defined __SUNPRO_C || defined __DECC || defined __HP_cc
 10+# pragma ident "@(#)$Header$"
 11+#endif
 12+
 13+#include <sys/types.h>
 14+#include <sys/socket.h>
 15+
 16+#include <netinet/in.h>
 17+
 18+#include <arpa/inet.h>
 19+
 20+#include <stdlib.h>
 21+#include <stdio.h>
 22+#include <string.h>
 23+#include <syslog.h>
 24+#include <errno.h>
 25+#include <strings.h>
 26+
 27+#include "willow.h"
 28+#include "wconfig.h"
 29+#include "wbackend.h"
 30+#include "wlog.h"
 31+#include "confparse.h"
 32+
 33+#define CONFIGFILE SYSCONFDIR "/willow.conf"
 34+
 35+int yyparse();
 36+
 37+struct listener **listeners;
 38+int nlisteners;
 39+struct configuration config;
 40+
 41+const char *current_file;
 42+
 43+void
 44+wconfig_init(const char *file)
 45+{
 46+ FILE *cfg;
 47+extern FILE *yyin;
 48+
 49+ if (file == NULL)
 50+ file = CONFIGFILE;
 51+ current_file = file;
 52+
 53+ if ((cfg = fopen(file, "r")) == NULL) {
 54+ perror(file);
 55+ exit(8);
 56+ }
 57+ wlog(WLOG_NOTICE, "loading configuration from %s", current_file);
 58+ yyin = cfg;
 59+ newconf_init();
 60+
 61+ if (yyparse()) {
 62+ wlog(WLOG_ERROR, "could not parse configuration file");
 63+ nerrors++;
 64+ }
 65+ if (!nlisteners) {
 66+ wlog(WLOG_ERROR, "no listeners defined");
 67+ nerrors++;
 68+ }
 69+ if (!nbackends) {
 70+ wlog(WLOG_ERROR, "no backends defined");
 71+ nerrors++;
 72+ }
 73+ if (nerrors) {
 74+ wlog(WLOG_ERROR, "%d error(s) in configuration file. cannot continue.", nerrors);
 75+ exit(8);
 76+ }
 77+
 78+ (void)fclose(cfg);
 79+}
 80+
 81+int
 82+add_listener(addr, port)
 83+ const char *addr;
 84+ int port;
 85+{
 86+struct listener *nl;
 87+
 88+ if (port < 0 || port > 65535) {
 89+ conf_report_error("invalid listener port %d", port);
 90+ nerrors++;
 91+ return -1;
 92+ }
 93+
 94+ if ((nl = wcalloc(1, sizeof(*nl))) == NULL)
 95+ outofmemory();
 96+
 97+ if ((listeners = wrealloc(listeners, sizeof(struct listener *) * ++nlisteners)) == NULL)
 98+ outofmemory();
 99+
 100+ nl->port = port;
 101+ nl->name = wstrdup(addr);
 102+ nl->addr.sin_family = AF_INET;
 103+ nl->addr.sin_port = htons(nl->port);
 104+ nl->addr.sin_addr.s_addr = inet_addr(nl->name);
 105+ listeners[nlisteners - 1] = nl;
 106+ wlog(WLOG_NOTICE, "listening on %s:%d", addr, port);
 107+ return 0;
 108+}
 109+
 110+int
 111+add_cachedir(dir, size)
 112+ const char *dir;
 113+ int size;
 114+{
 115+ if (size < 1) {
 116+ conf_report_error("invalid cache size %d\n", size);
 117+ nerrors++;
 118+ return -1;
 119+ }
 120+
 121+ config.caches = wrealloc(config.caches, sizeof(*config.caches) * (config.ncaches + 1));
 122+ config.caches[config.ncaches].dir = wstrdup(dir);
 123+ config.caches[config.ncaches].maxsize = size;
 124+ wlog(WLOG_NOTICE, "cache dir \"%s\", size %d bytes",
 125+ config.caches[config.ncaches].dir,
 126+ config.caches[config.ncaches].maxsize);
 127+ config.ncaches++;
 128+ return 0;
 129+}
Index: trunk/willow/src/willow/willow.c
@@ -0,0 +1,528 @@
 2+/* @(#) $Header$ */
 3+/* This source code is in the public domain. */
 4+/*
 5+ * Willow: Lightweight HTTP reverse-proxy.
 6+ */
 7+
 8+#if defined __SUNPRO_C || defined __DECC || defined __HP_cc
 9+# pragma ident "@(#)$Header$"
 10+#endif
 11+
 12+#include <sys/mman.h>
 13+
 14+#include <stdio.h>
 15+#include <stdlib.h>
 16+#include <signal.h>
 17+#include <stdarg.h>
 18+#include <string.h>
 19+#include <unistd.h>
 20+#include <errno.h>
 21+#include <pwd.h>
 22+#include <grp.h>
 23+#include <strings.h>
 24+#include <ctype.h>
 25+
 26+#include "wlog.h"
 27+#include "wnet.h"
 28+#include "wconfig.h"
 29+#include "willow.h"
 30+#include "whttp.h"
 31+#include "wcache.h"
 32+
 33+#ifdef WDEBUG_ALLOC
 34+static void ae_checkleaks(void);
 35+static void segv_action(int, siginfo_t *, void *);
 36+#endif
 37+
 38+static const char *progname;
 39+
 40+#define min(x,y) ((x) < (y) ? (x) : (y))
 41+
 42+/*ARGSUSED*/
 43+static void
 44+sig_exit(s)
 45+ int s;
 46+{
 47+ wnet_exit = 1;
 48+}
 49+
 50+static void
 51+usage(void)
 52+{
 53+ (void)fprintf(stderr, "usage: %s [-fzv]\n"
 54+ "\t-f\trun in foreground (don't detach)\n"
 55+ "\t-z\tcreate cache directory structure and exit\n"
 56+ "\t-v\tprint version number and exit\n"
 57+ , progname);
 58+}
 59+
 60+#ifdef __lint
 61+# pragma error_messages(off, E_H_C_CHECK2)
 62+#endif
 63+
 64+int
 65+main(argc, argv)
 66+ char *argv[];
 67+ int argc;
 68+{
 69+ int i;
 70+ int zflag = 0;
 71+ char *cfg = NULL;
 72+
 73+#ifdef WDEBUG_ALLOC
 74+struct sigaction segv_act;
 75+ bzero(&segv_act, sizeof(segv_act));
 76+ segv_act.sa_sigaction = segv_action;
 77+ segv_act.sa_flags = SA_SIGINFO;
 78+
 79+ sigaction(SIGSEGV, &segv_act, NULL);
 80+#endif
 81+
 82+ progname = argv[0];
 83+
 84+ while ((i = getopt(argc, argv, "fzvc:")) != -1) {
 85+ switch (i) {
 86+ case 'z':
 87+ zflag++;
 88+ /*FALLTHRU*/
 89+ case 'f':
 90+ config.foreground = 1;
 91+ break;
 92+ case 'v':
 93+ (void)fprintf(stderr, "%s\n", PACKAGE_VERSION);
 94+ exit(0);
 95+ /*NOTREACHED*/
 96+ case 'c':
 97+ cfg = optarg;
 98+ break;
 99+ default:
 100+ usage();
 101+ exit(8);
 102+ }
 103+ }
 104+
 105+ argv += optind;
 106+ argc -= optind;
 107+
 108+ if (argc) {
 109+ (void)fprintf(stderr, "%s: too many argments\n", progname);
 110+ usage();
 111+ exit(8);
 112+ }
 113+
 114+ wnet_set_time();
 115+
 116+ wconfig_init(cfg);
 117+
 118+ if (config.sgid) {
 119+ struct group *group = getgrnam(config.sgid);
 120+ if (!group) {
 121+ fprintf(stderr, "group %s does not exist", config.sgid);
 122+ exit(8);
 123+ }
 124+ if (setgid(group->gr_gid) < 0) {
 125+ perror("setgid");
 126+ exit(8);
 127+ }
 128+ }
 129+
 130+ if (config.suid) {
 131+ struct passwd *user = getpwnam(config.suid);
 132+ if (!user) {
 133+ fprintf(stderr, "user %s does not exist", config.suid);
 134+ exit(8);
 135+ }
 136+ if (setuid(user->pw_uid) < 0) {
 137+ perror("setuid");
 138+ exit(8);
 139+ }
 140+ }
 141+
 142+ wlog_init();
 143+ if (zflag) {
 144+ wcache_setupfs();
 145+ exit(0);
 146+ }
 147+
 148+ /*
 149+ * HTTP should be initialised before the network so that
 150+ * the wlogwriter exits cleanly.
 151+ */
 152+ whttp_init();
 153+ wnet_init();
 154+ wcache_init(1);
 155+
 156+ (void)signal(SIGINT, sig_exit);
 157+ (void)signal(SIGTERM, sig_exit);
 158+
 159+ wlog(WLOG_NOTICE, "running");
 160+
 161+#ifdef WDEBUG_ALLOC
 162+ (void)fprintf(stderr, "debug allocator enabled, assuming -f\n");
 163+ config.foreground = 1;
 164+#endif
 165+
 166+ if (!config.foreground)
 167+ daemon(0, 0);
 168+
 169+ wnet_run();
 170+ wlog_close();
 171+ wcache_shutdown();
 172+ whttp_shutdown();
 173+
 174+#ifdef WDEBUG_ALLOC
 175+ ae_checkleaks();
 176+#endif
 177+ return EXIT_SUCCESS;
 178+}
 179+
 180+#ifdef __lint
 181+# pragma error_messages(default, E_H_C_CHECK2)
 182+#endif
 183+
 184+void
 185+outofmemory(void)
 186+{
 187+ static int count;
 188+
 189+ if (count++)
 190+ abort();
 191+
 192+ wlog(WLOG_ERROR, "fatal: out of memory. exiting.");
 193+ exit(8);
 194+}
 195+
 196+void
 197+realloc_addchar(sp, c)
 198+ char **sp;
 199+ int c;
 200+{
 201+ char *p;
 202+ int len;
 203+
 204+ if (*sp)
 205+ len = strlen(*sp);
 206+ else
 207+ len = 0;
 208+
 209+ if ((*sp = wrealloc(*sp, len + 2)) == NULL)
 210+ outofmemory();
 211+ p = *sp + len;
 212+ *p++ = (char) c;
 213+ *p++ = '\0';
 214+}
 215+
 216+void
 217+realloc_strcat(sp, s)
 218+ char **sp;
 219+ const char *s;
 220+{
 221+ int len;
 222+
 223+ if (*sp)
 224+ len = strlen(*sp);
 225+ else
 226+ len = 1;
 227+ if ((*sp = wrealloc(*sp, len + strlen(s) + 1)) == NULL)
 228+ outofmemory();
 229+ (void)strcat(*sp, s);
 230+}
 231+
 232+char **
 233+wstrvec(str, sep, lim)
 234+ const char *str, *sep;
 235+ int lim;
 236+{
 237+ char **result = NULL;
 238+ int nres = 0;
 239+ char *s;
 240+const char *st = str;
 241+
 242+ while ((!lim || --lim) && (s = strstr(st, sep))) {
 243+ result = wrealloc(result, ++nres * sizeof(char *));
 244+ while (isspace(*st))
 245+ st++;
 246+ result[nres - 1] = wmalloc((s - st) + 1);
 247+ memcpy(result[nres - 1], st, s - st);
 248+ result[nres - 1][s - st] = '\0';
 249+ st = s + strlen(sep);
 250+ }
 251+
 252+ result = wrealloc(result, ++nres * sizeof(char *));
 253+ while (isspace(*st))
 254+ st++;
 255+ result[nres - 1] = wstrdup(st);
 256+
 257+ result = wrealloc(result, (nres + 1) * sizeof(char *));
 258+ result[nres] = NULL;
 259+ return result;
 260+}
 261+
 262+void wstrvecfree(vec)
 263+ char **vec;
 264+{
 265+ char **s = vec;
 266+ while (*s) {
 267+ wfree(*s);
 268+ s++;
 269+ }
 270+ wfree(vec);
 271+}
 272+
 273+
 274+int char_table[256] = {
 275+ /* 0 */ 0, 0, 0, 0, 0, 0, 0, 0,
 276+ /* 8 */ 0, 0, 0, 0, 0, 0, 0, 0,
 277+ /* 16 */ 0, 0, 0, 0, 0, 0, 0, 0,
 278+ /* 24 */ 0, 0, 0, 0, 0, 0, 0, 0,
 279+ /* 32 */ 0, 0, 0, 0, 0, 0, 0, 0,
 280+ /* 40 */ 0, 0, 0, 0, 0, 0, CHAR_HOST, 0,
 281+ /* 48 */ CHAR_HOST, CHAR_HOST, CHAR_HOST, CHAR_HOST,
 282+ /* 52 */ CHAR_HOST, CHAR_HOST, CHAR_HOST, CHAR_HOST,
 283+ /* 56 */ CHAR_HOST, CHAR_HOST, CHAR_HOST, 0,
 284+ /* 60 */ 0, 0, 0, 0,
 285+ /* 64 */ 0, CHAR_HOST, CHAR_HOST, CHAR_HOST,
 286+ /* 68 */ CHAR_HOST, CHAR_HOST, CHAR_HOST, CHAR_HOST,
 287+ /* 72 */ CHAR_HOST, CHAR_HOST, CHAR_HOST, CHAR_HOST,
 288+ /* 76 */ CHAR_HOST, CHAR_HOST, CHAR_HOST, CHAR_HOST,
 289+ /* 80 */ CHAR_HOST, CHAR_HOST, CHAR_HOST, CHAR_HOST,
 290+ /* 84 */ CHAR_HOST, CHAR_HOST, CHAR_HOST, CHAR_HOST,
 291+ /* 88 */ CHAR_HOST, CHAR_HOST, CHAR_HOST, 0,
 292+ /* 92 */ 0, 0, 0, 0,
 293+ /* 96 */ 0, CHAR_HOST, CHAR_HOST, CHAR_HOST,
 294+ /* 100 */ CHAR_HOST, CHAR_HOST, CHAR_HOST, CHAR_HOST,
 295+ /* 104 */ CHAR_HOST, CHAR_HOST, CHAR_HOST, CHAR_HOST,
 296+ /* 108 */ CHAR_HOST, CHAR_HOST, CHAR_HOST, CHAR_HOST,
 297+ /* 112 */ CHAR_HOST, CHAR_HOST, CHAR_HOST, CHAR_HOST,
 298+ /* 116 */ CHAR_HOST, CHAR_HOST, CHAR_HOST, CHAR_HOST,
 299+ /* 120 */ CHAR_HOST, CHAR_HOST, 0, 0,
 300+ /* 124 */ 0, 0, 0, 0,
 301+ /* 136 */ 0, 0, 0, 0, 0, 0, 0, 0,
 302+ /* 144 */ 0, 0, 0, 0, 0, 0, 0, 0,
 303+ /* 152 */ 0, 0, 0, 0, 0, 0, 0, 0,
 304+ /* 160 */ 0, 0, 0, 0, 0, 0, 0, 0,
 305+ /* 168 */ 0, 0, 0, 0, 0, 0, 0, 0,
 306+ /* 176 */ 0, 0, 0, 0, 0, 0, 0, 0,
 307+ /* 184 */ 0, 0, 0, 0, 0, 0, 0, 0,
 308+ /* 192 */ 0, 0, 0, 0, 0, 0, 0, 0,
 309+ /* 200 */ 0, 0, 0, 0, 0, 0, 0, 0,
 310+ /* 208 */ 0, 0, 0, 0, 0, 0, 0, 0,
 311+ /* 216 */ 0, 0, 0, 0, 0, 0, 0, 0,
 312+ /* 224 */ 0, 0, 0, 0, 0, 0, 0, 0,
 313+ /* 232 */ 0, 0, 0, 0, 0, 0, 0, 0,
 314+ /* 240 */ 0, 0, 0, 0, 0, 0, 0, 0,
 315+ /* 248 */ 0, 0, 0, 0, 0, 0, 0, 0,
 316+};
 317+
 318+#ifdef WDEBUG_ALLOC
 319+
 320+struct alloc_entry {
 321+ char *ae_addr;
 322+ char *ae_mapping;
 323+ size_t ae_mapsize;
 324+ size_t ae_size;
 325+ int ae_freed;
 326+ const char *ae_freed_file;
 327+ int ae_freed_line;
 328+ const char *ae_alloced_file;
 329+ int ae_alloced_line;
 330+struct alloc_entry *ae_next;
 331+};
 332+
 333+static struct alloc_entry allocs;
 334+static int pgsize;
 335+
 336+static void
 337+segv_action(sig, si, data)
 338+ int sig;
 339+ siginfo_t *si;
 340+ void *data;
 341+{
 342+struct alloc_entry *ae;
 343+
 344+ /*
 345+ * This is mostly non-standard, unportable and unreliable, but if the debug allocator
 346+ * is enabled, it's more important to produce useful errors than conform to the letter
 347+ * of the law.
 348+ */
 349+ (void)fprintf(stderr, "SEGV at %p%s (pid %d)\n", si->si_addr,
 350+#ifdef SI_NOINFO
 351+ si->si_code == SI_NOINFO ? " [SI_NOINFO]" : "",
 352+#else
 353+ "",
 354+#endif
 355+ (int) getpid());
 356+ for (ae = allocs.ae_next; ae; ae = ae->ae_next)
 357+ if (/*!ae->ae_freed &&*/ (char *)si->si_addr > ae->ae_mapping &&
 358+ (char *)si->si_addr < ae->ae_mapping + ae->ae_mapsize) {
 359+ (void)fprintf(stderr, "\t%p [map @ %p size %d] from %s:%d\n", ae->ae_addr, ae->ae_mapping,
 360+ ae->ae_mapsize, ae->ae_alloced_file, ae->ae_alloced_line);
 361+ break;
 362+ }
 363+ if (ae == NULL)
 364+ (void)fprintf(stderr, "\tunknown address\n");
 365+ abort();
 366+ _exit(1);
 367+}
 368+
 369+static void
 370+ae_checkleaks(void)
 371+{
 372+struct alloc_entry *ae;
 373+
 374+ for (ae = allocs.ae_next; ae; ae = ae->ae_next)
 375+ if (!ae->ae_freed)
 376+ (void)fprintf(stderr, "%p @ %s:%d\n", ae->ae_addr, ae->ae_alloced_file, ae->ae_alloced_line);
 377+}
 378+
 379+void *
 380+internal_wmalloc(size, file, line)
 381+ size_t size;
 382+ const char *file;
 383+ int line;
 384+{
 385+ void *p;
 386+struct alloc_entry *ae;
 387+ size_t mapsize;
 388+
 389+ if (pgsize == 0)
 390+ pgsize = sysconf(_SC_PAGESIZE);
 391+
 392+ mapsize = (size/pgsize + 2) * pgsize;
 393+ if ((p = mmap(NULL, mapsize, PROT_READ|PROT_WRITE, MAP_PRIVATE | MAP_ANON, -1, 0)) == (void *)-1) {
 394+ (void)fprintf(stderr, "mmap: %s\n", strerror(errno));
 395+ return NULL;
 396+ }
 397+
 398+ for (ae = &allocs; ae->ae_next; ae = ae->ae_next)
 399+ if (ae->ae_next->ae_mapping == p)
 400+ break;
 401+
 402+ if (!ae->ae_next) {
 403+ if ((ae->ae_next = malloc(sizeof(struct alloc_entry))) == NULL) {
 404+ (void)fputs("out of memory\n", stderr);
 405+ abort();
 406+ }
 407+ bzero(ae->ae_next, sizeof(struct alloc_entry));
 408+ }
 409+
 410+ ae = ae->ae_next;
 411+ ae->ae_addr = ((char *)p + (mapsize - pgsize)) - size;
 412+ ae->ae_mapping = p;
 413+ ae->ae_mapsize = mapsize;
 414+ ae->ae_size = size;
 415+ ae->ae_freed = 0;
 416+ ae->ae_alloced_file = file;
 417+ ae->ae_alloced_line = line;
 418+#if 0
 419+ (void)fprintf(stderr, "alloc %d @ %p [map @ %p:%p, size %d] at %s:%d\n", size, ae->ae_addr,
 420+ ae->ae_mapping, ae->ae_mapping + ae->ae_mapsize, ae->ae_mapsize, file, line);
 421+#endif
 422+ if (mprotect(ae->ae_addr + size, pgsize, PROT_NONE) < 0) {
 423+ (void)fprintf(stderr, "mprotect(0x%p, %d, PROT_NONE): %s\n", ae->ae_addr + size, pgsize, strerror(errno));
 424+ exit(8);
 425+ }
 426+
 427+ return ae->ae_addr;
 428+}
 429+
 430+void
 431+internal_wfree(p, file, line)
 432+ void *p;
 433+ const char *file;
 434+ int line;
 435+{
 436+struct alloc_entry *ae;
 437+
 438+#if 0
 439+ (void)fprintf(stderr, "free %p @ %s:%d\n", p, file, line);
 440+#endif
 441+ if (!p)
 442+ return;
 443+
 444+ for (ae = allocs.ae_next; ae; ae = ae->ae_next) {
 445+ if (ae->ae_addr == p) {
 446+ if (ae->ae_freed) {
 447+ (void)fprintf(stderr, "wfree: ptr %p already freed @ %s:%d! [alloced at %s:%d]\n",
 448+ p, ae->ae_freed_file, ae->ae_freed_line,
 449+ ae->ae_alloced_file, ae->ae_alloced_line);
 450+ ae_checkleaks();
 451+ abort();
 452+ }
 453+ ae->ae_freed = 1;
 454+ ae->ae_freed_file = file;
 455+ ae->ae_freed_line = line;
 456+ if (mprotect(ae->ae_addr + ae->ae_size, pgsize, PROT_READ | PROT_WRITE) < 0) {
 457+ (void)fprintf(stderr, "mprotect(0x%p, %d, PROT_READ | PROT_WRITE): %s\n",
 458+ ae->ae_addr + ae->ae_size, pgsize, strerror(errno));
 459+ exit(8);
 460+ }
 461+ munmap(ae->ae_mapping, ae->ae_mapsize);
 462+ return;
 463+ }
 464+ }
 465+
 466+ (void)fprintf(stderr, "wfree: ptr %p never malloced! [%s:%d]\n", p, file, line);
 467+ ae_checkleaks();
 468+ abort();
 469+}
 470+
 471+char *
 472+internal_wstrdup(s, file, line)
 473+ const char *s, *file;
 474+ int line;
 475+{
 476+ char *ret = internal_wmalloc(strlen(s) + 1, file, line);
 477+ (void)strcpy(ret, s);
 478+ return ret;
 479+}
 480+
 481+void *
 482+internal_wrealloc(p, size, file, line)
 483+ void *p;
 484+ const char *file;
 485+ int line;
 486+ size_t size;
 487+{
 488+ void *new;
 489+struct alloc_entry *ae;
 490+ size_t osize = 0;
 491+
 492+ if (!p)
 493+ return internal_wmalloc(size, file, line);
 494+
 495+ for (ae = allocs.ae_next; ae; ae = ae->ae_next)
 496+ if (ae->ae_addr == p) {
 497+ osize = ae->ae_size;
 498+ break;
 499+ }
 500+
 501+ if (osize == 0) {
 502+ (void)fprintf(stderr, "wrealloc: ptr %p never malloced!\n", p);
 503+ ae_checkleaks();
 504+ abort();
 505+ }
 506+
 507+ new = internal_wmalloc(size, file, line);
 508+ bcopy(p, new, min(osize, size));
 509+ internal_wfree(p, file, line);
 510+
 511+ return new;
 512+}
 513+
 514+void *
 515+internal_wcalloc(num, size, file, line)
 516+ size_t num, size;
 517+ const char *file;
 518+ int line;
 519+{
 520+ size_t t = size * num;
 521+ void *p;
 522+
 523+ if ((p = internal_wmalloc(t, __FILE__, __LINE__)) == NULL)
 524+ return NULL;
 525+ bzero(p, t);
 526+ return p;
 527+}
 528+
 529+#endif