r17089 MediaWiki - Code Review archive

Repository:MediaWiki
Revision:r17088‎ | r17089 | r17090 >
Date:17:58, 18 October 2006
Author:river
Status:old
Tags:
Comment:
convert to C++
replace lame config parser with two-pass parser allowing rehash
Modified paths:
  • /trunk/willow/configure.in (modified) (history)
  • /trunk/willow/mk/prog.mk.in (modified) (history)
  • /trunk/willow/mk/rules.mk.in (modified) (history)
  • /trunk/willow/mk/vars.mk.in (modified) (history)
  • /trunk/willow/src/include/confparse.h (modified) (history)
  • /trunk/willow/src/include/wbackend.h (modified) (history)
  • /trunk/willow/src/include/wcache.h (modified) (history)
  • /trunk/willow/src/include/wconfig.h (modified) (history)
  • /trunk/willow/src/include/willow.h (modified) (history)
  • /trunk/willow/src/include/wlog.h (modified) (history)
  • /trunk/willow/src/willow/Makefile.in (modified) (history)
  • /trunk/willow/src/willow/confparse.c (deleted) (history)
  • /trunk/willow/src/willow/confparse.cc (added) (history)
  • /trunk/willow/src/willow/lexer.l (modified) (history)
  • /trunk/willow/src/willow/parser.y (modified) (history)
  • /trunk/willow/src/willow/wbackend.c (deleted) (history)
  • /trunk/willow/src/willow/wbackend.cc (added) (history)
  • /trunk/willow/src/willow/wcache.c (deleted) (history)
  • /trunk/willow/src/willow/wcache.cc (added) (history)
  • /trunk/willow/src/willow/wconfig.c (deleted) (history)
  • /trunk/willow/src/willow/wconfig.cc (added) (history)
  • /trunk/willow/src/willow/whttp.c (deleted) (history)
  • /trunk/willow/src/willow/whttp.cc (added) (history)
  • /trunk/willow/src/willow/whttp_entity.c (deleted) (history)
  • /trunk/willow/src/willow/whttp_entity.cc (added) (history)
  • /trunk/willow/src/willow/willow.c (deleted) (history)
  • /trunk/willow/src/willow/willow.cc (added) (history)
  • /trunk/willow/src/willow/wlog.cc (added) (history)
  • /trunk/willow/src/willow/wnet.cc (added) (history)
  • /trunk/willow/src/willow/wnet_libevent.cc (added) (history)

Diff [purge]

Index: trunk/willow/configure.in
@@ -1,8 +1,8 @@
22 # @(#) $Header$
33
4 -AC_INIT(willow, 1.0-cvs, keturner@livejournal.com)
 4+AC_INIT(willow, 1.0-cvs, river@attenuate.org)
55
6 -USER_CFLAGS="$CFLAGS"
 6+USER_CXXFLAGS="$CXXFLAGS"
77
88 # autoconf brokenness
99 if test -z "$CC"; then
@@ -10,48 +10,49 @@
1111 fi
1212
1313 gcc_common="-W -Wall -Wno-unused -g"
14 -debug_cflags_gcc="$gcc_common -O0"
15 -prod_cflags_gcc="$gcc_common -O3"
 14+debug_cxxflags_gcc="$gcc_common -O0"
 15+prod_cxxflags_gcc="$gcc_common -O3"
1616 sunpro_common="-errshort=tags -errtags -mc -xc99"
17 -debug_cflags_sunpro="-g $sunpro_common -v -xs"
18 -prod_cflags_sunpro="-fast $sunpro_common -xdepend=yes -xipo=2 -xalias_level=std"
 17+debug_cxxflags_sunpro="-g $sunpro_common -v -xs"
 18+prod_cxxflags_sunpro="-fast $sunpro_common -xdepend=yes -xipo=2 -xalias_level=std"
1919
2020 AC_CONFIG_HEADER(src/include/config.h)
2121
2222 echo ""
2323 echo "examining environment..."
24 -AC_LANG_C
 24+AC_LANG(C++)
2525 AC_PROG_CC
 26+AC_PROG_CXX
2627
2728 if test x${ac_compiler_gnu} = xyes; then
28 - debug_cflags=$debug_cflags_gcc
29 - prod_cflags=$prod_cflags_gcc
 29+ debug_cxxflags=$debug_cxxflags_gcc
 30+ prod_cxxflags=$prod_cxxflags_gcc
3031 fi
3132
3233 AC_MSG_CHECKING(whether we are using the SunPro C compiler)
3334 if $CC -V 2>&1 | grep "Sun C" >/dev/null; then
3435 AC_MSG_RESULT(yes)
35 - debug_cflags=$debug_cflags_sunpro
36 - prod_cflags=$prod_cflags_sunpro
 36+ debug_cxxflags=$debug_cxxflags_sunpro
 37+ prod_cxxflags=$prod_cxxflags_sunpro
3738 else
3839 AC_MSG_RESULT(no)
3940 fi
4041
41 -mycflags=$prod_cflags
 42+mycxxflags=$prod_cxxflags
4243 AC_ARG_ENABLE(debug, AC_HELP_STRING([--enable-debug],[Compile with debug support]),
4344 [ if test $enableval = yes; then
4445 AC_DEFINE([WILLOW_DEBUG], [], [Compile with debug support])
45 - mycflags=$debug_cflags
 46+ mycxxflags=$debug_cxxflags
4647 fi
4748 ])
4849
49 -if test -z "$USER_CFLAGS"; then
50 - USE_CFLAGS=$mycflags
 50+if test -z "$USER_CXXFLAGS"; then
 51+ USE_CXXFLAGS=$mycxxflags
5152 else
52 - USE_CFLAGS=$USER_CFLAGS
 53+ USE_CXXFLAGS=$USER_CXXFLAGS
5354 fi
5455
55 -echo "using CFLAGS: $USE_CFLAGS"
 56+echo "using CXXFLAGS: $USE_CXXFLAGS"
5657
5758 AC_PROG_INSTALL
5859 AC_MAKE_INCLUDE
@@ -128,7 +129,7 @@
129130 AC_CHECK_LIB(event, event_add, [],[AC_ERROR([libevent not found])])
130131
131132 datadir="${datadir}/willow"
132 -CFLAGS="$USE_CFLAGS"
 133+CXXFLAGS="$USE_CXXFLAGS"
133134
134135
135136 PATH_LIBEXECDIR=`eval echo $libexecdir | sed -e "s,NONE,$prefix,"`
Index: trunk/willow/mk/prog.mk.in
@@ -10,15 +10,15 @@
1111 _PROGRAM=$(PROGRAM)
1212 all: $(_PROGRAM)
1313
14 -OBJS=$(SRCS:.c=.o) $(OBJADD)
 14+_ALLOBJS=$(OBJS) $(OBJADD)
1515
16 -$(_PROGRAM): $(OBJS)
 16+$(_PROGRAM): $(_ALLOBJS)
1717 @echo " $(_LINK) -o $@"
1818 @$(_LINK) -o $@
1919
2020 clean:
21 - @echo " $(_RMF) $(_PROGRAM) $(OBJS)"
22 - @$(_RMF) $(_PROGRAM) $(OBJS)
 21+ @echo " $(_RMF) $(_PROGRAM) $(_ALLOBJS)"
 22+ @$(_RMF) $(_PROGRAM) $(_ALLOBJS) y.tab.cc lex.yy.cc y.tab.h
2323
2424 install:
2525 @echo " $(_MKDIR) $(BINDIR)"
Index: trunk/willow/mk/rules.mk.in
@@ -8,6 +8,10 @@
99 @echo " $(_COMPILE) -c $<"
1010 @$(_COMPILE) -c $<
1111
 12+.cc.o:
 13+ @echo " $(_COMPILEXX) -c $<"
 14+ @$(_COMPILEXX) -c $<
 15+
1216 _dist:
1317 [ -d $(DISTDEST)/$(_DISTPATH) ] || mkdir -p $(DISTDEST)/$(_DISTPATH)
1418 cp $(_MYDISTFILES) $(DISTDEST)/$(_DISTPATH)
Index: trunk/willow/mk/vars.mk.in
@@ -12,14 +12,17 @@
1313 _YACC= @YACC@
1414
1515 CC= @CC@
 16+CXX= @CXX@
1617 _CPPFLAGS= @CPPFLAGS@
1718 _CFLAGS= @CFLAGS@
 19+_CXXFLAGS= @CXXFLAGS@
1820 _LDFLAGS= @LDFLAGS@ @LIBS@
1921
2022 SRCROOT= @abs_top_srcdir@
2123
22 -_LINK= $(CC) $(_CFLAGS) $(CFLAGS) $(_LDFLAGS) $(OBJS) $(LDFLAGS)
 24+_LINK= $(CXX) $(_CFLAGS) $(CFLAGS) $(_LDFLAGS) $(_ALLOBJS) $(LDFLAGS)
2325 _COMPILE= $(CC) $(_CPPFLAGS) $(CPPFLAGS) $(_CFLAGS) $(CFLAGS)
 26+_COMPILEXX= $(CXX) $(_CPPFLAGS) $(CPPFLAGS) $(_CXXFLAGS) $(CXXFLAGS)
2427 _RMF= rm -f
2528 _AR= ar
2629 _RANLIB= ranlib
Index: trunk/willow/src/include/wcache.h
@@ -36,7 +36,7 @@
3737 time_t co_time; /* Last-Modified / retrieval time */
3838 time_t co_lru; /* Last used timestamp, for eviction */
3939 int co_id; /* Object id */
40 - int co_plen; /* Size of cache object path */
 40+ size_t co_plen; /* Size of cache object path */
4141 size_t co_size; /* Object size */
4242 char *co_path; /* Object data location */
4343 int co_complete; /* Finished being cached */
Index: trunk/willow/src/include/wconfig.h
@@ -15,14 +15,18 @@
1616 #include <sys/types.h>
1717 #include <netinet/in.h>
1818
 19+#include <string>
 20+using std::string;
 21+#include <vector>
 22+using std::vector;
 23+
1924 struct listener {
20 - char *name;
21 - char *host;
 25+ string name;
 26+ string host;
2227 int port;
23 -struct sockaddr_in addr;
 28+struct sockaddr_in addr;
2429 };
25 -extern int nlisteners;
26 -extern struct listener **listeners;
 30+extern vector<listener *> listeners;
2731
2832 struct cachedir {
2933 char *dir;
@@ -31,22 +35,22 @@
3236
3337 extern struct configuration {
3438 int foreground;
35 -const char *access_log;
 39+ string access_log;
3640 struct cachedir *caches;
3741 int ncaches;
3842 time_t cache_expevery;
3943 int cache_expthresh;
40 - char *suid, *sgid;
41 - int compress;
 44+ string suid, sgid;
 45+ bool compress;
4246 int complevel;
43 - int backend_retry;
44 - int cache_private;
45 - int use_carp;
 47+ time_t backend_retry;
 48+ bool cache_private;
 49+ bool use_carp;
4650 } config;
4751
48 -void wconfig_init(const char *);
 52+void wconfig_init(char const *);
4953
50 -int add_listener(const char *, int);
51 -int add_cachedir(const char *, int);
 54+int add_listener(string const &, int);
 55+int add_cachedir(string const &, int);
5256
5357 #endif
Index: trunk/willow/src/include/willow.h
@@ -14,8 +14,20 @@
1515
1616 #include "config.h"
1717
18 -typedef long long w_size_t;
 18+#include <sstream>
1919
 20+template<typename To, typename From>
 21+To lexical_cast(From const &f)
 22+{
 23+std::stringstream strm;
 24+To t;
 25+ strm << f;
 26+ strm >> t;
 27+ return t;
 28+}
 29+
 30+typedef unsigned long long w_size_t;
 31+
2032 #ifdef WDEBUG_ALLOC
2133 void *internal_wmalloc(size_t, const char *, int);
2234 void internal_wfree(void *, const char *, int);
@@ -52,10 +64,10 @@
5365 #endif
5466
5567 #ifndef HAVE_STRLCAT
56 -size_t strlcat(char *dst, const char *src, size_t siz);
 68+extern "C" size_t strlcat(char *dst, const char *src, size_t siz);
5769 #endif
5870 #ifndef HAVE_STRLCPY
59 -size_t strlcpy(char *dst, const char *src, size_t siz);
 71+extern "C" size_t strlcpy(char *dst, const char *src, size_t siz);
6072 #endif
6173
6274 void outofmemory(void);
@@ -63,8 +75,6 @@
6476 # pragma does_not_return(outofmemory)
6577 #endif
6678
67 -#define min(x,y) ((x) < (y) ? (x) : (y))
68 -
6979 #define CHAR_HOST 1
7080
7181 extern int char_table[];
Index: trunk/willow/src/include/wbackend.h
@@ -13,16 +13,21 @@
1414 #endif
1515
1616 #include <sys/types.h>
17 -
1817 #include <netinet/in.h>
1918
 19+#include <string>
 20+using std::string;
 21+#include <vector>
 22+using std::vector;
 23+
2024 struct fde;
2125
2226 struct backend {
23 - char *be_name; /* IP as specified in config */
 27+ backend(string const &, string const &, int);
 28+ string be_name; /* IP as specified in config */
2429 int be_port; /* port number */
2530 struct sockaddr_in be_addr; /* socket address */
26 - char *be_straddr; /* formatted address */
 31+ string be_straddr; /* formatted address */
2732 int be_dead; /* 0 if okay, 1 if unavailable */
2833 time_t be_time; /* If dead, time to retry */
2934 uint32_t be_hash; /* constant carp "host" hash */
@@ -33,11 +38,11 @@
3439
3540 typedef void (*backend_cb)(struct backend *, struct fde *, void *);
3641
37 -void add_backend(const char *, int);
38 -void backend_file(char *);
 42+void add_backend(string const &, int);
 43+void backend_file(string const &);
3944
40 -int get_backend(const char *url, backend_cb, void *, int);
 45+int get_backend(string const &url, backend_cb, void *, int);
4146
42 -extern int nbackends;
 47+extern vector<backend *> backends;
4348
4449 #endif
Index: trunk/willow/src/include/confparse.h
@@ -1,6 +1,7 @@
22 /* @(#) $Header$ */
33 /* From: $Nightmare: nightmare/include/config.h,v 1.32.2.2.2.2 2002/07/02 03:41:28 ejb Exp $ */
44 /* From: newconf.h,v 7.36 2005/03/21 22:42:10 leeh Exp */
 5+/* From: newconf.h 2651 2006-10-13 18:54:49Z river */
56 /* This source code is in the public domain. */
67 /*
78 * Willow: Lightweight HTTP reverse-proxy.
@@ -14,65 +15,382 @@
1516 # pragma ident "@(#)$Header$"
1617 #endif
1718
18 -#include "queue.h"
 19+#include <string>
 20+#include <map>
 21+#include <vector>
 22+#include <assert.h>
1923
20 -struct conf_entry {
21 -const char *cf_name;
22 - int cf_type;
23 - void (*cf_func) (void *);
24 - int cf_len;
25 - void *cf_arg;
26 - LIST_ENTRY(conf_entry) entries;
 24+using std::string;
 25+using std::map;
 26+using std::vector;
 27+
 28+#include "willow.h"
 29+
 30+/*
 31+ * the config tree.
 32+ *
 33+ * it's structured as a tree, e.g. this config file:
 34+ *
 35+ * server {
 36+ * name = "foo";
 37+ * };
 38+ * oper "bar" {
 39+ * class = "opers";
 40+ * };
 41+ *
 42+ * would produce the keys:
 43+ *
 44+ * /server/name = "foo"
 45+ * /oper=bar/class = "opers"
 46+ *
 47+ * consumers can either look up a key by name, or traverse the tree
 48+ * and receive all keys below a certain point.
 49+ */
 50+
 51+namespace conf {
 52+
 53+extern string linebuf;
 54+extern int curpos;
 55+extern string current_file;
 56+extern int lineno;
 57+extern int conf_parse_error;
 58+
 59+struct declpos {
 60+ declpos(string const &file_, int line_, int pos_)
 61+ : file(file_), line(line_), pos(pos_) {}
 62+ declpos() : file(""), line(0), pos(0) {}
 63+
 64+ string format(void) const {
 65+ return file + "(" + lexical_cast<std::string>(line) + ")";
 66+ }
 67+
 68+ static declpos here() {
 69+ return declpos(current_file, lineno, curpos);
 70+ }
 71+
 72+ string file;
 73+ int line, pos;
2774 };
2875
29 -struct top_conf {
30 - char *tc_name;
31 - int (*tc_sfunc) (struct top_conf *);
32 - int (*tc_efunc) (struct top_conf *);
33 - LIST_HEAD(tc_items_head, conf_entry) tc_items;
34 - struct conf_entry *tc_entries;
35 - LIST_ENTRY(top_conf) entries;
 76+enum cv_type {
 77+ cv_int = 1,
 78+ cv_string,
 79+ cv_qstring,
 80+ cv_time,
 81+ cv_yesno
3682 };
3783
38 -#define CF_QSTRING 0x01
39 -#define CF_INT 0x02
40 -#define CF_STRING 0x03
41 -#define CF_TIME 0x04
42 -#define CF_YESNO 0x05
43 -#define CF_LIST 0x06
44 -#define CF_ONE 0x07
 84+struct avalue {
 85+ avalue();
4586
46 -#define CF_MTYPE 0xFF
 87+ string av_strval;
 88+ long av_intval;
 89+ int av_type;
 90+};
4791
48 -#define CF_FLIST 0x1000
49 -#define CF_MFLAG 0xFF00
 92+struct value {
 93+ value(declpos const &pos);
5094
51 -typedef struct conf_parm_t_stru
52 -{
53 -struct conf_parm_t_stru *next;
54 - int type;
55 - union {
56 - char *string;
57 - int number;
58 - struct conf_parm_t_stru *list;
59 - } v;
60 -} conf_parm_t;
 95+ void report_error (const char *, ...) const;
 96+ void vreport_error (const char *, va_list) const;
 97+ size_t nvalues (void) const;
 98+ bool is_single (cv_type) const;
6199
62 -extern struct top_conf *conf_cur_block;
 100+ string cv_name;
 101+ vector<avalue> cv_values; /* list of conf_avalue_t */
 102+ bool cv_touched; /* 1 if someone touched this item */
 103+ declpos cv_pos;
 104+};
63105
64 -extern const char *current_file;
65 -extern int lineno;
66 -extern int nerrors;
 106+struct tree_entry {
 107+ tree_entry(declpos const &);
 108+ void report_error (const char *, ...) const;
 109+ void vreport_error (const char *, va_list) const;
 110+ value *operator/ (string const &value);
 111+ void add (value const &);
67112
68 -int read_config(char *);
69 -int conf_start_block(const char *, const char *);
70 -int conf_end_block(struct top_conf *);
71 -int conf_call_set(struct top_conf *, char *, conf_parm_t *, int);
72 -void conf_report_error(const char *, ...);
73 -void newconf_init(void);
74 -extern char *conf_cur_block_name;
75 -int add_conf_item(const char *topconf, const char *name, int type, void (*func) (void *));
76 -int remove_conf_item(const char *topconf, const char *name);
 113+ string item_name; /* e.g. "oper" */
 114+ string item_key; /* e.g. "bar" */
 115+ map<string, value> item_values;
 116+ declpos item_pos;
 117+ bool item_unnamed;
 118+ bool item_touched;
 119+ bool item_is_template;
 120+};
77121
 122+struct tree {
 123+ void reset();
 124+ /*
 125+ * lookup a key by name. warning, this is slow! it should only
 126+ * be called once per rehash, cache the value somewhere (for the
 127+ * core ircd this is done in ConfigFileEntry etc.)
 128+ *
 129+ * conf_find_tree_entry only finds keys with no name.
 130+ * conf_find_named_tree_entry finds a key with a name (e.g.
 131+ * conf_find_named_tree_entry("operator", "god")).
 132+ *
 133+ * if found, the key is touched.
 134+ * if the key doesn't exist, returns NULL.
 135+ *
 136+ * conf_iterate_tree_entries() finds all the entries of a certain type. the
 137+ * void* parameter is used to store state; pass it in as NULL on the first call.
 138+ * NULL is returned after the last matching entry. all iterated entries are
 139+ * touched.
 140+ *
 141+ */
 142+ bool add (tree_entry const &);
 143+ tree_entry *find_item (tree_entry const &);
 144+ tree_entry *find (string const &key);
 145+ tree_entry *find (string const &key, string const &name);
 146+ tree_entry *find_or_new(string const &block, string const &name, declpos const &pos,
 147+ bool unnamed, bool is_template);
78148
 149+ vector<tree_entry> entries;
 150+};
 151+
 152+extern tree global_conf_tree;
 153+#ifdef NEED_PARSING_TREE
 154+extern tree parsing_tree;
79155 #endif
 156+
 157+tree *parse_file(string const &file);
 158+
 159+string type_name(cv_type);
 160+
 161+typedef bool (*function) (tree_entry &, value &);
 162+
 163+struct value_definer;
 164+struct block_definer;
 165+struct conf_definer;
 166+
 167+template<typename ret>
 168+struct callable {
 169+ virtual ~callable() {}
 170+ virtual ret operator()(tree_entry &, value &) const = 0;
 171+};
 172+
 173+template<typename ret>
 174+struct function_callable : callable<ret> {
 175+ function_callable(ret (*f_)(tree_entry &, value &))
 176+ : f(f_) {}
 177+
 178+ ret operator()(tree_entry &e, value &v) const {
 179+ return f(e, v);
 180+ }
 181+
 182+ ret (*f) (tree_entry &, value &);
 183+};
 184+typedef callable<bool> value_validator;
 185+typedef callable<void> value_setter;
 186+
 187+template<typename ret>
 188+struct ender {
 189+ virtual ~ender() {}
 190+ virtual ret operator() (tree_entry &) const = 0;
 191+};
 192+
 193+template<typename ret>
 194+struct function_ender : ender<ret> {
 195+ function_ender(ret (*f_) (tree_entry &))
 196+ : f(f_) {}
 197+ ret operator() (tree_entry &e) const {
 198+ return f(e);
 199+ }
 200+ ret (*f) (tree_entry &);
 201+};
 202+
 203+template<typename ret>
 204+function_callable<ret> func(ret (*f)(tree_entry &, value &)) {
 205+ return function_callable<ret>(f);
 206+}
 207+template<typename ret>
 208+function_ender<ret> func(ret (*f)(tree_entry &)) {
 209+ return function_ender<ret>(f);
 210+}
 211+
 212+template<cv_type type>
 213+struct simple_value : callable<bool> {
 214+ bool operator() (tree_entry &e, value &v) const {
 215+ if (!v.is_single(type)) {
 216+ v.report_error("expected single %s", type_name(type).c_str());
 217+ return false;
 218+ }
 219+ return true;
 220+ }
 221+};
 222+typedef simple_value<cv_int> simple_int;
 223+typedef simple_value<cv_yesno> simple_yesno;
 224+typedef simple_value<cv_time> simple_time;
 225+
 226+struct simple_range : callable<bool> {
 227+ simple_range(int min_, int max_) : min(min_), max(max_) {}
 228+ bool operator() (tree_entry &e, value &v) const {
 229+ if (!v.is_single(cv_int)) {
 230+ v.report_error("expected single integer");
 231+ return false;
 232+ }
 233+ int i = v.cv_values[0].av_intval;
 234+ if (i < min || i > max) {
 235+ v.report_error("value must be between %d and %d", min, max);
 236+ return false;
 237+ }
 238+ return true;
 239+ }
 240+ int min, max;
 241+};
 242+
 243+template<cv_type string_type>
 244+struct nonempty_astring : callable<bool> {
 245+ bool operator() (tree_entry &e, value &v) const {
 246+ if (!v.is_single(string_type)) {
 247+ v.report_error("expected single string");
 248+ return false;
 249+ }
 250+ if (v.cv_values[0].av_strval.empty()) {
 251+ v.report_error("expected non-empty string");
 252+ return false;
 253+ }
 254+ return true;
 255+ }
 256+};
 257+typedef nonempty_astring<cv_string> nonempty_string;
 258+typedef nonempty_astring<cv_qstring> nonempty_qstring;
 259+
 260+template<typename T>
 261+struct set_simple : callable<void> {
 262+ set_simple(T& sv_) : sv(sv_) {}
 263+ void operator() (tree_entry &e, value &v) const {
 264+ sv = v.cv_values[0].av_intval;
 265+ }
 266+ T& sv;
 267+};
 268+struct set_astring : callable<void> {
 269+ set_astring(string& sv_) : sv(sv_) {}
 270+ void operator() (tree_entry &e, value &v) const {
 271+ sv = v.cv_values[0].av_strval;
 272+ }
 273+ string& sv;
 274+};
 275+
 276+typedef set_astring set_string;
 277+typedef set_astring set_qstring;
 278+typedef set_simple<time_t> set_time;
 279+typedef set_simple<bool> set_yesno;
 280+typedef set_simple<int> set_int;
 281+
 282+struct accept_any : callable<bool> {
 283+ bool operator() (tree_entry &e, value &v) const {
 284+ return true;
 285+ }
 286+};
 287+
 288+struct ignore : callable<void> {
 289+ void operator() (tree_entry &e, value &v) const {
 290+ }
 291+};
 292+
 293+struct conf_definer {
 294+ conf_definer() {};
 295+ block_definer &block(string const &name, int flags = 0);
 296+ vector<block_definer *> blocks;
 297+
 298+ bool validate(tree &) const;
 299+ void set(tree &) const;
 300+};
 301+
 302+struct value_definer {
 303+ template<typename Vt, typename St>
 304+ value_definer(string const &name_, Vt const &v_, St const &s_) {
 305+ vv = new Vt(v_);
 306+ vs = new St(s_);
 307+ }
 308+
 309+ bool validate(tree_entry &e, value &v);
 310+ void set(tree_entry &e, value &v);
 311+
 312+ value_validator const *vv;
 313+ value_setter const *vs;
 314+};
 315+
 316+extern const int require_name;
 317+
 318+struct block_definer {
 319+ block_definer(conf_definer &parent_, string const &name, int flags);
 320+
 321+ template<typename Vt, typename St>
 322+ block_definer &value(string const &name, Vt const &v, St const &s) {
 323+ values.insert(make_pair(name, new value_definer(name, v, s)));
 324+ return *this;
 325+ }
 326+ template<typename Vt, typename St>
 327+ block_definer &end(Vt vefn_, St sefn_) {
 328+ vefn = new Vt(vefn_);
 329+ sefn = new St(sefn_);
 330+ return *this;
 331+ }
 332+ template<typename St>
 333+ block_definer &end(St sefn_) {
 334+ sefn = new St(sefn_);
 335+ return *this;
 336+ }
 337+
 338+ block_definer &block(string const &name, int flags = 0);
 339+ bool validate(tree_entry &e);
 340+ void set(tree_entry &e);
 341+
 342+ conf_definer &parent;
 343+ string name;
 344+ map<string, value_definer *> values;
 345+ ender<bool> *vefn;
 346+ ender<void> *sefn;
 347+ int flags;
 348+};
 349+
 350+/* get the first string or int value from an avalue */
 351+#define CONF_FIRST(value) ((value).cv_values[0])
 352+#define CONF_ASTRVAL(value) ( \
 353+ assert((value).is_single(cv_string) \
 354+ || (value).is_single(cv_qstring)), \
 355+ CONF_FIRST(value).av_strval)
 356+
 357+#define CONF_AINTVAL(value) ( \
 358+ assert((value).is_single(cv_int) \
 359+ || (value).is_single(cv_time) \
 360+ || (value).is_single(cv_yesno)), \
 361+ CONF_FIRST(value).av_intval)
 362+
 363+/*
 364+ * these are mostly internal to the configuration parser.
 365+ */
 366+
 367+tree_entry *new_tree_entry_from_template(tree &t, string const &,
 368+ string const &, string const &,
 369+ declpos const &, bool unnamed, bool is_template);
 370+value *value_from_variable(string const &name, string const &varname,
 371+ declpos const &);
 372+
 373+void report_parse_error (const char *, ...);
 374+void catastrophic_error (const char *, ...);
 375+void confparse_init (void);
 376+void handle_pragma (string const &);
 377+bool if_true (string const &);
 378+void add_variable (value *value);
 379+void add_variable_simple (const char *, const char *);
 380+bool find_include (string &);
 381+
 382+struct item_entry {
 383+ item_entry(declpos const &pos_, value *val_)
 384+ : where(pos_), val(val_) {}
 385+
 386+ declpos where;
 387+ conf::value *val;
 388+};
 389+
 390+} // namespace conf
 391+
 392+extern FILE *yyin; /* same */
 393+extern "C" int yylex(void); /* same */
 394+extern "C" int yyparse(void); /* from parser */
 395+extern "C" void yyerror(const char *);
 396+
 397+#endif
Index: trunk/willow/src/include/wlog.h
@@ -12,7 +12,9 @@
1313 # pragma ident "@(#)$Header$"
1414 #endif
1515
16 -#include <stdio.h>
 16+#include <cstdio>
 17+#include <string>
 18+using std::string;
1719
1820 #include "config.h"
1921
@@ -23,11 +25,11 @@
2426 #define WLOG_MAX 3
2527
2628 extern struct log_variables {
27 - char *file;
28 - int level;
29 - FILE *fp;
30 - int syslog;
31 - int facility;
 29+ string file;
 30+ int level;
 31+ FILE *fp;
 32+ bool syslog;
 33+ int facility;
3234 } logging;
3335
3436 void wlog_init(void);
Index: trunk/willow/src/willow/wcache.c
@@ -1,546 +0,0 @@
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/wconfig.c
@@ -1,128 +0,0 @@
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
@@ -1,528 +0,0 @@
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
Index: trunk/willow/src/willow/wbackend.c
@@ -1,321 +0,0 @@
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
@@ -1,515 +0,0 @@
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/whttp_entity.c
@@ -1,1255 +0,0 @@
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/whttp.c
@@ -1,848 +0,0 @@
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/willow.cc
@@ -0,0 +1,520 @@
 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+#include "confparse.h"
 33+
 34+#ifdef WDEBUG_ALLOC
 35+static void ae_checkleaks(void);
 36+static void segv_action(int, siginfo_t *, void *);
 37+#endif
 38+
 39+static const char *progname;
 40+
 41+#define min(x,y) ((x) < (y) ? (x) : (y))
 42+
 43+/*ARGSUSED*/
 44+static void
 45+sig_exit(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(int argc, char *argv[])
 66+{
 67+ int i;
 68+ int zflag = 0;
 69+ char *cfg = NULL;
 70+
 71+#ifdef WDEBUG_ALLOC
 72+struct sigaction segv_act;
 73+ bzero(&segv_act, sizeof(segv_act));
 74+ segv_act.sa_sigaction = segv_action;
 75+ segv_act.sa_flags = SA_SIGINFO;
 76+
 77+ sigaction(SIGSEGV, &segv_act, NULL);
 78+#endif
 79+
 80+ progname = argv[0];
 81+
 82+ while ((i = getopt(argc, argv, "fzvc:")) != -1) {
 83+ switch (i) {
 84+ case 'z':
 85+ zflag++;
 86+ /*FALLTHRU*/
 87+ case 'f':
 88+ config.foreground = 1;
 89+ break;
 90+ case 'v':
 91+ (void)fprintf(stderr, "%s\n", PACKAGE_VERSION);
 92+ exit(0);
 93+ /*NOTREACHED*/
 94+ case 'c':
 95+ cfg = optarg;
 96+ break;
 97+ default:
 98+ usage();
 99+ exit(8);
 100+ }
 101+ }
 102+
 103+ argv += optind;
 104+ argc -= optind;
 105+
 106+ if (argc) {
 107+ (void)fprintf(stderr, "%s: too many argments\n", progname);
 108+ usage();
 109+ exit(8);
 110+ }
 111+
 112+ wnet_set_time();
 113+
 114+ wconfig_init(cfg);
 115+
 116+ if (config.sgid != "") {
 117+ struct group *group = getgrnam(config.sgid.c_str());
 118+ if (!group) {
 119+ fprintf(stderr, "group %s does not exist", config.sgid.c_str());
 120+ exit(8);
 121+ }
 122+ if (setgid(group->gr_gid) < 0) {
 123+ perror("setgid");
 124+ exit(8);
 125+ }
 126+ }
 127+
 128+ if (config.suid != "") {
 129+ struct passwd *user = getpwnam(config.suid.c_str());
 130+ if (!user) {
 131+ fprintf(stderr, "user %s does not exist", config.suid.c_str());
 132+ exit(8);
 133+ }
 134+ if (setuid(user->pw_uid) < 0) {
 135+ perror("setuid");
 136+ exit(8);
 137+ }
 138+ }
 139+
 140+ wlog_init();
 141+ if (zflag) {
 142+ wcache_setupfs();
 143+ exit(0);
 144+ }
 145+
 146+ /*
 147+ * HTTP should be initialised before the network so that
 148+ * the wlogwriter exits cleanly.
 149+ */
 150+ whttp_init();
 151+ wnet_init();
 152+ wcache_init(1);
 153+
 154+ (void)signal(SIGINT, sig_exit);
 155+ (void)signal(SIGTERM, sig_exit);
 156+
 157+ wlog(WLOG_NOTICE, "running");
 158+
 159+#ifdef WDEBUG_ALLOC
 160+ (void)fprintf(stderr, "debug allocator enabled, assuming -f\n");
 161+ config.foreground = 1;
 162+#endif
 163+
 164+ if (!config.foreground)
 165+ daemon(0, 0);
 166+
 167+ wnet_run();
 168+ wlog_close();
 169+ wcache_shutdown();
 170+ whttp_shutdown();
 171+
 172+#ifdef WDEBUG_ALLOC
 173+ ae_checkleaks();
 174+#endif
 175+ return EXIT_SUCCESS;
 176+}
 177+
 178+#ifdef __lint
 179+# pragma error_messages(default, E_H_C_CHECK2)
 180+#endif
 181+
 182+void
 183+outofmemory(void)
 184+{
 185+ static int count;
 186+
 187+ if (count++)
 188+ abort();
 189+
 190+ wlog(WLOG_ERROR, "fatal: out of memory. exiting.");
 191+ exit(8);
 192+}
 193+
 194+void
 195+realloc_addchar(char **sp, int c)
 196+{
 197+ char *p;
 198+ int len;
 199+
 200+ if (*sp)
 201+ len = strlen(*sp);
 202+ else
 203+ len = 0;
 204+
 205+ if ((*sp = (char *)wrealloc(*sp, len + 2)) == NULL)
 206+ outofmemory();
 207+ p = *sp + len;
 208+ *p++ = (char) c;
 209+ *p++ = '\0';
 210+}
 211+
 212+void
 213+realloc_strcat(char **sp, const char *s)
 214+{
 215+ int len;
 216+
 217+ if (*sp)
 218+ len = strlen(*sp);
 219+ else
 220+ len = 1;
 221+ if ((*sp = (char *)wrealloc(*sp, len + strlen(s) + 1)) == NULL)
 222+ outofmemory();
 223+ (void)strcat(*sp, s);
 224+}
 225+
 226+char **
 227+wstrvec(const char *str, const char *sep, int lim)
 228+{
 229+ char **result = NULL;
 230+ int nres = 0;
 231+ char *s;
 232+const char *st = str;
 233+
 234+ while ((!lim || --lim) && (s = strstr(st, sep))) {
 235+ result = (char **)wrealloc(result, ++nres * sizeof(char *));
 236+ while (isspace(*st))
 237+ st++;
 238+ result[nres - 1] = (char *)wmalloc((s - st) + 1);
 239+ memcpy(result[nres - 1], st, s - st);
 240+ result[nres - 1][s - st] = '\0';
 241+ st = s + strlen(sep);
 242+ }
 243+
 244+ result = (char **)wrealloc(result, ++nres * sizeof(char *));
 245+ while (isspace(*st))
 246+ st++;
 247+ result[nres - 1] = wstrdup(st);
 248+
 249+ result = (char **)wrealloc(result, (nres + 1) * sizeof(char *));
 250+ result[nres] = NULL;
 251+ return result;
 252+}
 253+
 254+void
 255+wstrvecfree(char **vec)
 256+{
 257+ char **s = vec;
 258+ while (*s) {
 259+ wfree(*s);
 260+ s++;
 261+ }
 262+ wfree(vec);
 263+}
 264+
 265+
 266+int char_table[256] = {
 267+ /* 0 */ 0, 0, 0, 0, 0, 0, 0, 0,
 268+ /* 8 */ 0, 0, 0, 0, 0, 0, 0, 0,
 269+ /* 16 */ 0, 0, 0, 0, 0, 0, 0, 0,
 270+ /* 24 */ 0, 0, 0, 0, 0, 0, 0, 0,
 271+ /* 32 */ 0, 0, 0, 0, 0, 0, 0, 0,
 272+ /* 40 */ 0, 0, 0, 0, 0, 0, CHAR_HOST, 0,
 273+ /* 48 */ CHAR_HOST, CHAR_HOST, CHAR_HOST, CHAR_HOST,
 274+ /* 52 */ CHAR_HOST, CHAR_HOST, CHAR_HOST, CHAR_HOST,
 275+ /* 56 */ CHAR_HOST, CHAR_HOST, CHAR_HOST, 0,
 276+ /* 60 */ 0, 0, 0, 0,
 277+ /* 64 */ 0, CHAR_HOST, CHAR_HOST, CHAR_HOST,
 278+ /* 68 */ CHAR_HOST, CHAR_HOST, CHAR_HOST, CHAR_HOST,
 279+ /* 72 */ CHAR_HOST, CHAR_HOST, CHAR_HOST, CHAR_HOST,
 280+ /* 76 */ CHAR_HOST, CHAR_HOST, CHAR_HOST, CHAR_HOST,
 281+ /* 80 */ CHAR_HOST, CHAR_HOST, CHAR_HOST, CHAR_HOST,
 282+ /* 84 */ CHAR_HOST, CHAR_HOST, CHAR_HOST, CHAR_HOST,
 283+ /* 88 */ CHAR_HOST, CHAR_HOST, CHAR_HOST, 0,
 284+ /* 92 */ 0, 0, 0, 0,
 285+ /* 96 */ 0, CHAR_HOST, CHAR_HOST, CHAR_HOST,
 286+ /* 100 */ CHAR_HOST, CHAR_HOST, CHAR_HOST, CHAR_HOST,
 287+ /* 104 */ CHAR_HOST, CHAR_HOST, CHAR_HOST, CHAR_HOST,
 288+ /* 108 */ CHAR_HOST, CHAR_HOST, CHAR_HOST, CHAR_HOST,
 289+ /* 112 */ CHAR_HOST, CHAR_HOST, CHAR_HOST, CHAR_HOST,
 290+ /* 116 */ CHAR_HOST, CHAR_HOST, CHAR_HOST, CHAR_HOST,
 291+ /* 120 */ CHAR_HOST, CHAR_HOST, 0, 0,
 292+ /* 124 */ 0, 0, 0, 0,
 293+ /* 136 */ 0, 0, 0, 0, 0, 0, 0, 0,
 294+ /* 144 */ 0, 0, 0, 0, 0, 0, 0, 0,
 295+ /* 152 */ 0, 0, 0, 0, 0, 0, 0, 0,
 296+ /* 160 */ 0, 0, 0, 0, 0, 0, 0, 0,
 297+ /* 168 */ 0, 0, 0, 0, 0, 0, 0, 0,
 298+ /* 176 */ 0, 0, 0, 0, 0, 0, 0, 0,
 299+ /* 184 */ 0, 0, 0, 0, 0, 0, 0, 0,
 300+ /* 192 */ 0, 0, 0, 0, 0, 0, 0, 0,
 301+ /* 200 */ 0, 0, 0, 0, 0, 0, 0, 0,
 302+ /* 208 */ 0, 0, 0, 0, 0, 0, 0, 0,
 303+ /* 216 */ 0, 0, 0, 0, 0, 0, 0, 0,
 304+ /* 224 */ 0, 0, 0, 0, 0, 0, 0, 0,
 305+ /* 232 */ 0, 0, 0, 0, 0, 0, 0, 0,
 306+ /* 240 */ 0, 0, 0, 0, 0, 0, 0, 0,
 307+ /* 248 */ 0, 0, 0, 0, 0, 0, 0, 0,
 308+};
 309+
 310+#ifdef WDEBUG_ALLOC
 311+
 312+struct alloc_entry {
 313+ char *ae_addr;
 314+ char *ae_mapping;
 315+ size_t ae_mapsize;
 316+ size_t ae_size;
 317+ int ae_freed;
 318+ const char *ae_freed_file;
 319+ int ae_freed_line;
 320+ const char *ae_alloced_file;
 321+ int ae_alloced_line;
 322+struct alloc_entry *ae_next;
 323+};
 324+
 325+static struct alloc_entry allocs;
 326+static int pgsize;
 327+
 328+static void
 329+segv_action(sig, si, data)
 330+ int sig;
 331+ siginfo_t *si;
 332+ void *data;
 333+{
 334+struct alloc_entry *ae;
 335+
 336+ /*
 337+ * This is mostly non-standard, unportable and unreliable, but if the debug allocator
 338+ * is enabled, it's more important to produce useful errors than conform to the letter
 339+ * of the law.
 340+ */
 341+ (void)fprintf(stderr, "SEGV at %p%s (pid %d)\n", si->si_addr,
 342+#ifdef SI_NOINFO
 343+ si->si_code == SI_NOINFO ? " [SI_NOINFO]" : "",
 344+#else
 345+ "",
 346+#endif
 347+ (int) getpid());
 348+ for (ae = allocs.ae_next; ae; ae = ae->ae_next)
 349+ if (/*!ae->ae_freed &&*/ (char *)si->si_addr > ae->ae_mapping &&
 350+ (char *)si->si_addr < ae->ae_mapping + ae->ae_mapsize) {
 351+ (void)fprintf(stderr, "\t%p [map @ %p size %d] from %s:%d\n", ae->ae_addr, ae->ae_mapping,
 352+ ae->ae_mapsize, ae->ae_alloced_file, ae->ae_alloced_line);
 353+ break;
 354+ }
 355+ if (ae == NULL)
 356+ (void)fprintf(stderr, "\tunknown address\n");
 357+ abort();
 358+ _exit(1);
 359+}
 360+
 361+static void
 362+ae_checkleaks(void)
 363+{
 364+struct alloc_entry *ae;
 365+
 366+ for (ae = allocs.ae_next; ae; ae = ae->ae_next)
 367+ if (!ae->ae_freed)
 368+ (void)fprintf(stderr, "%p @ %s:%d\n", ae->ae_addr, ae->ae_alloced_file, ae->ae_alloced_line);
 369+}
 370+
 371+void *
 372+internal_wmalloc(size, file, line)
 373+ size_t size;
 374+ const char *file;
 375+ int line;
 376+{
 377+ void *p;
 378+struct alloc_entry *ae;
 379+ size_t mapsize;
 380+
 381+ if (pgsize == 0)
 382+ pgsize = sysconf(_SC_PAGESIZE);
 383+
 384+ mapsize = (size/pgsize + 2) * pgsize;
 385+ if ((p = mmap(NULL, mapsize, PROT_READ|PROT_WRITE, MAP_PRIVATE | MAP_ANON, -1, 0)) == (void *)-1) {
 386+ (void)fprintf(stderr, "mmap: %s\n", strerror(errno));
 387+ return NULL;
 388+ }
 389+
 390+ for (ae = &allocs; ae->ae_next; ae = ae->ae_next)
 391+ if (ae->ae_next->ae_mapping == p)
 392+ break;
 393+
 394+ if (!ae->ae_next) {
 395+ if ((ae->ae_next = malloc(sizeof(struct alloc_entry))) == NULL) {
 396+ (void)fputs("out of memory\n", stderr);
 397+ abort();
 398+ }
 399+ bzero(ae->ae_next, sizeof(struct alloc_entry));
 400+ }
 401+
 402+ ae = ae->ae_next;
 403+ ae->ae_addr = ((char *)p + (mapsize - pgsize)) - size;
 404+ ae->ae_mapping = p;
 405+ ae->ae_mapsize = mapsize;
 406+ ae->ae_size = size;
 407+ ae->ae_freed = 0;
 408+ ae->ae_alloced_file = file;
 409+ ae->ae_alloced_line = line;
 410+#if 0
 411+ (void)fprintf(stderr, "alloc %d @ %p [map @ %p:%p, size %d] at %s:%d\n", size, ae->ae_addr,
 412+ ae->ae_mapping, ae->ae_mapping + ae->ae_mapsize, ae->ae_mapsize, file, line);
 413+#endif
 414+ if (mprotect(ae->ae_addr + size, pgsize, PROT_NONE) < 0) {
 415+ (void)fprintf(stderr, "mprotect(0x%p, %d, PROT_NONE): %s\n", ae->ae_addr + size, pgsize, strerror(errno));
 416+ exit(8);
 417+ }
 418+
 419+ return ae->ae_addr;
 420+}
 421+
 422+void
 423+internal_wfree(p, file, line)
 424+ void *p;
 425+ const char *file;
 426+ int line;
 427+{
 428+struct alloc_entry *ae;
 429+
 430+#if 0
 431+ (void)fprintf(stderr, "free %p @ %s:%d\n", p, file, line);
 432+#endif
 433+ if (!p)
 434+ return;
 435+
 436+ for (ae = allocs.ae_next; ae; ae = ae->ae_next) {
 437+ if (ae->ae_addr == p) {
 438+ if (ae->ae_freed) {
 439+ (void)fprintf(stderr, "wfree: ptr %p already freed @ %s:%d! [alloced at %s:%d]\n",
 440+ p, ae->ae_freed_file, ae->ae_freed_line,
 441+ ae->ae_alloced_file, ae->ae_alloced_line);
 442+ ae_checkleaks();
 443+ abort();
 444+ }
 445+ ae->ae_freed = 1;
 446+ ae->ae_freed_file = file;
 447+ ae->ae_freed_line = line;
 448+ if (mprotect(ae->ae_addr + ae->ae_size, pgsize, PROT_READ | PROT_WRITE) < 0) {
 449+ (void)fprintf(stderr, "mprotect(0x%p, %d, PROT_READ | PROT_WRITE): %s\n",
 450+ ae->ae_addr + ae->ae_size, pgsize, strerror(errno));
 451+ exit(8);
 452+ }
 453+ munmap(ae->ae_mapping, ae->ae_mapsize);
 454+ return;
 455+ }
 456+ }
 457+
 458+ (void)fprintf(stderr, "wfree: ptr %p never malloced! [%s:%d]\n", p, file, line);
 459+ ae_checkleaks();
 460+ abort();
 461+}
 462+
 463+char *
 464+internal_wstrdup(s, file, line)
 465+ const char *s, *file;
 466+ int line;
 467+{
 468+ char *ret = internal_wmalloc(strlen(s) + 1, file, line);
 469+ (void)strcpy(ret, s);
 470+ return ret;
 471+}
 472+
 473+void *
 474+internal_wrealloc(p, size, file, line)
 475+ void *p;
 476+ const char *file;
 477+ int line;
 478+ size_t size;
 479+{
 480+ void *new;
 481+struct alloc_entry *ae;
 482+ size_t osize = 0;
 483+
 484+ if (!p)
 485+ return internal_wmalloc(size, file, line);
 486+
 487+ for (ae = allocs.ae_next; ae; ae = ae->ae_next)
 488+ if (ae->ae_addr == p) {
 489+ osize = ae->ae_size;
 490+ break;
 491+ }
 492+
 493+ if (osize == 0) {
 494+ (void)fprintf(stderr, "wrealloc: ptr %p never malloced!\n", p);
 495+ ae_checkleaks();
 496+ abort();
 497+ }
 498+
 499+ new = internal_wmalloc(size, file, line);
 500+ bcopy(p, new, min(osize, size));
 501+ internal_wfree(p, file, line);
 502+
 503+ return new;
 504+}
 505+
 506+void *
 507+internal_wcalloc(num, size, file, line)
 508+ size_t num, size;
 509+ const char *file;
 510+ int line;
 511+{
 512+ size_t t = size * num;
 513+ void *p;
 514+
 515+ if ((p = internal_wmalloc(t, __FILE__, __LINE__)) == NULL)
 516+ return NULL;
 517+ bzero(p, t);
 518+ return p;
 519+}
 520+
 521+#endif
Index: trunk/willow/src/willow/wnet.cc
@@ -0,0 +1,433 @@
 2+/* @(#) $Header$ */
 3+/* This source code is in the public domain. */
 4+/*
 5+ * Willow: Lightweight HTTP reverse-proxy.
 6+ * wnet: Networking.
 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 "config.h"
 17+#ifdef HAVE_SYS_SENDFILE_H
 18+# include <sys/sendfile.h>
 19+#endif
 20+
 21+#include <arpa/inet.h>
 22+
 23+#include <stdio.h>
 24+#include <string.h>
 25+#include <stdlib.h>
 26+#include <unistd.h>
 27+#include <errno.h>
 28+#include <fcntl.h>
 29+#include <signal.h>
 30+#include <assert.h>
 31+#include <strings.h>
 32+#include <time.h>
 33+
 34+#include "willow.h"
 35+#include "wnet.h"
 36+#include "wconfig.h"
 37+#include "wlog.h"
 38+#include "whttp.h"
 39+
 40+#define RDBUF_INC 8192 /* buffer in 8 KiB incrs */
 41+
 42+struct wrtbuf {
 43+ /* for buffers only */
 44+const void *wb_buf;
 45+ /* for sendfile only */
 46+ off_t wb_off;
 47+ int wb_source;
 48+ /* for buffers & sendfile */
 49+ size_t wb_size;
 50+ int wb_done;
 51+ fdwcb wb_func;
 52+ void *wb_udata;
 53+};
 54+
 55+char current_time_str[30];
 56+char current_time_short[30];
 57+#ifdef __lint
 58+# pragma error_messages(off, E_GLOBAL_COULD_BE_STATIC)
 59+#endif
 60+time_t current_time;
 61+#ifdef __lint
 62+# pragma error_messages(default, E_GLOBAL_COULD_BE_STATIC)
 63+#endif
 64+
 65+static void init_fde(struct fde *);
 66+
 67+static void wnet_accept(struct fde *);
 68+static void wnet_write_do(struct fde *);
 69+static void wnet_sendfile_do(struct fde *);
 70+
 71+static void readbuf_reset(struct readbuf *);
 72+
 73+struct fde *fde_table;
 74+int max_fd;
 75+
 76+int wnet_exit;
 77+
 78+void
 79+wnet_init(void)
 80+{
 81+size_t i;
 82+
 83+ max_fd = getdtablesize();
 84+ if ((fde_table = (fde *)wcalloc(max_fd, sizeof(struct fde))) == NULL)
 85+ outofmemory();
 86+
 87+ wlog(WLOG_NOTICE, "maximum number of open files: %d", max_fd);
 88+
 89+ (void)signal(SIGPIPE, SIG_IGN);
 90+ wnet_init_select();
 91+
 92+ for (i = 0; i < listeners.size(); ++i) {
 93+ struct listener *lns = listeners[i];
 94+
 95+ int fd = wnet_open("listener");
 96+ int one = 1;
 97+ if (setsockopt(fd, SOL_SOCKET, SO_REUSEADDR, &one, sizeof(one)) == -1) {
 98+ wlog(WLOG_ERROR, "setsockopt: %s: %s\n", lns->name.c_str(), strerror(errno));
 99+ exit(8);
 100+ }
 101+ if (bind(fd, (struct sockaddr *) &lns->addr, sizeof(lns->addr)) < 0) {
 102+ wlog(WLOG_ERROR, "bind: %s: %s\n", lns->name.c_str(), strerror(errno));
 103+ exit(8);
 104+ }
 105+ if (listen(fd, 10) < 0) {
 106+ wlog(WLOG_ERROR, "listen: %s: %s\n", lns->name.c_str(), strerror(errno));
 107+ exit(8);
 108+ }
 109+ wnet_register(fd, FDE_READ, wnet_accept, NULL);
 110+ wlog(WLOG_NOTICE, "listening on %s", lns->name.c_str());
 111+ }
 112+}
 113+
 114+void
 115+wnet_accept(fde *e)
 116+{
 117+struct client_data *cdata;
 118+#ifdef __hpux
 119+ int addrlen;
 120+#else
 121+ socklen_t addrlen;
 122+#endif
 123+ int newfd, val;
 124+struct fde *newe;
 125+
 126+ if ((cdata = (client_data *)wcalloc(1, sizeof(*cdata))) == NULL)
 127+ outofmemory();
 128+
 129+ addrlen = sizeof(cdata->cdat_addr);
 130+
 131+ if ((newfd = accept(e->fde_fd, (struct sockaddr *) &cdata->cdat_addr, &addrlen)) < 0) {
 132+ wlog(WLOG_NOTICE, "accept error: %s", strerror(errno));
 133+ wfree(cdata);
 134+ return;
 135+ }
 136+
 137+ if (newfd >= max_fd) {
 138+ wlog(WLOG_NOTICE, "out of file descriptors!");
 139+ wfree(cdata);
 140+ (void)close(newfd);
 141+ return;
 142+ }
 143+
 144+ val = fcntl(newfd, F_GETFL, 0);
 145+ if (val == -1 || fcntl(newfd, F_SETFL, val | O_NONBLOCK) == -1) {
 146+ wlog(WLOG_WARNING, "fcntl(%d) failed: %s", newfd, strerror(errno));
 147+ wfree(cdata);
 148+ (void)close(newfd);
 149+ return;
 150+ }
 151+
 152+ newe = &fde_table[newfd];
 153+ init_fde(newe);
 154+ newe->fde_flags.open = 1;
 155+ newe->fde_fd = newfd;
 156+ newe->fde_cdata = cdata;
 157+ newe->fde_desc = "accept()ed fd";
 158+ (void)inet_ntop(AF_INET, &cdata->cdat_addr.sin_addr.s_addr, newe->fde_straddr, sizeof(newe->fde_straddr));
 159+
 160+ WDEBUG((WLOG_DEBUG, "wnet_accept: new fd %d", newfd));
 161+ http_new(newe);
 162+ return;
 163+}
 164+
 165+static void
 166+init_fde(fde *fde)
 167+{
 168+ bzero(fde, sizeof(*fde));
 169+ fde->fde_desc = "<unknown>";
 170+ (void)strcpy(fde->fde_straddr, "NONE");
 171+}
 172+
 173+int
 174+wnet_open(const char *desc)
 175+{
 176+ int fd, val;
 177+
 178+ if ((fd = socket(PF_INET, SOCK_STREAM, IPPROTO_TCP)) < 0) {
 179+ wlog(WLOG_WARNING, "socket: %s", strerror(errno));
 180+ return -1;
 181+ }
 182+
 183+ val = fcntl(fd, F_GETFL, 0);
 184+ if (val == -1 || fcntl(fd, F_SETFL, val | O_NONBLOCK) == -1) {
 185+ wlog(WLOG_WARNING, "fcntl(%d) failed: %s", fd, strerror(errno));
 186+ return -1;
 187+ }
 188+
 189+ init_fde(&fde_table[fd]);
 190+ fde_table[fd].fde_fd = fd;
 191+ fde_table[fd].fde_desc = desc;
 192+ fde_table[fd].fde_flags.open = 1;
 193+
 194+ return fd;
 195+}
 196+
 197+void
 198+wnet_set_blocking(int fd)
 199+{
 200+ int val;
 201+
 202+ val = fcntl(fd, F_GETFL, 0);
 203+ if (val == -1 || fcntl(fd, F_SETFL, val & ~O_NONBLOCK) == -1)
 204+ wlog(WLOG_WARNING, "fcntl(%d) failed: %s", fd, strerror(errno));
 205+}
 206+
 207+void
 208+wnet_close(int fd)
 209+{
 210+struct fde *e = &fde_table[fd];
 211+ assert(e->fde_flags.open);
 212+ WDEBUG((WLOG_DEBUG, "close fd %d [%s]", e->fde_fd, e->fde_desc));
 213+ wnet_register(fd, FDE_READ | FDE_WRITE, NULL, NULL);
 214+ (void)close(e->fde_fd);
 215+ if (e->fde_cdata)
 216+ wfree(e->fde_cdata);
 217+ readbuf_free(&e->fde_readbuf);
 218+ e->fde_flags.open = 0;
 219+ e->fde_read_handler = NULL;
 220+ e->fde_write_handler = NULL;
 221+}
 222+
 223+int
 224+wnet_sendfile(int fd, int source, size_t size, off_t off, fdwcb cb, void *data, int flags)
 225+{
 226+struct wrtbuf *wb;
 227+struct fde *e = &fde_table[fd];
 228+
 229+ WDEBUG((WLOG_DEBUG, "wnet_sendfile: %d (+%ld) bytes from %d to %d [%s]",
 230+ size, (long)off, source, fd, e->fde_desc));
 231+
 232+ if ((wb = (wrtbuf *)wcalloc(1, sizeof(*wb))) == NULL) {
 233+ wlog(WLOG_WARNING, "out of memory");
 234+ return -1;
 235+ }
 236+
 237+ wb->wb_done = 0;
 238+ wb->wb_func = cb;
 239+ wb->wb_udata = data;
 240+ wb->wb_size = size;
 241+ wb->wb_source = source;
 242+ wb->wb_off = off;
 243+
 244+ e->fde_wdata = wb;
 245+ wnet_register(e->fde_fd, FDE_WRITE, wnet_sendfile_do, e);
 246+ wnet_sendfile_do(e);
 247+ return 0;
 248+}
 249+
 250+void
 251+wnet_write(int fd, const void *buf, size_t bufsz, fdwcb cb, void *data, int flags)
 252+{
 253+struct wrtbuf *wb;
 254+struct fde *e = &fde_table[fd];
 255+
 256+ WDEBUG((WLOG_DEBUG, "wnet_write: %d bytes to %d [%s]", bufsz, e->fde_fd, e->fde_desc));
 257+
 258+ if ((wb = (wrtbuf *)wmalloc(sizeof(*wb))) == NULL)
 259+ outofmemory();
 260+
 261+ wb->wb_buf = buf;
 262+ wb->wb_size = bufsz;
 263+ wb->wb_done = 0;
 264+ wb->wb_func = cb;
 265+ wb->wb_udata = data;
 266+
 267+ e->fde_wdata = wb;
 268+
 269+ wnet_register(e->fde_fd, FDE_WRITE, wnet_write_do, e);
 270+ wnet_write_do(e);
 271+}
 272+
 273+static void
 274+wnet_write_do(fde *e)
 275+{
 276+struct wrtbuf *buf;
 277+ int i;
 278+#ifdef WILLOW_DEBUG
 279+ char *p;
 280+#endif
 281+
 282+ buf = (wrtbuf *)e->fde_wdata;
 283+ while ((i = write(e->fde_fd, (char *)buf->wb_buf + buf->wb_done, buf->wb_size - buf->wb_done)) > -1) {
 284+#ifdef WILLOW_DEBUG_no
 285+ (void)fprintf(stderr, "write buf: [");
 286+ for (p = ((char *)buf->wb_buf + buf->wb_done); p < ((char *)buf->wb_buf + buf->wb_done + i); ++p)
 287+ (void)fputc(*p, stderr);
 288+ (void)fputs("]\n", stderr);
 289+#endif
 290+ buf->wb_done += i;
 291+ WDEBUG((WLOG_DEBUG, "%d of %d done", buf->wb_done, buf->wb_size));
 292+ if (buf->wb_done == (off_t)buf->wb_size) {
 293+ wnet_register(e->fde_fd, FDE_WRITE, NULL, NULL);
 294+ buf->wb_func(e, buf->wb_udata, 0);
 295+ wfree(buf);
 296+ return;
 297+ }
 298+ }
 299+
 300+ if (errno == EWOULDBLOCK)
 301+ return;
 302+
 303+ wnet_register(e->fde_fd, FDE_WRITE, NULL, NULL);
 304+ buf->wb_func(e, buf->wb_udata, -1);
 305+ wfree(buf);
 306+}
 307+
 308+static void
 309+wnet_sendfile_do(fde *e)
 310+{
 311+struct wrtbuf *buf;
 312+ int i;
 313+ /*LINTED unused variable: freebsd-only*/
 314+ off_t off, origoff;
 315+
 316+ (void)off;
 317+
 318+ buf = (wrtbuf *)e->fde_wdata;
 319+ origoff = buf->wb_off;
 320+
 321+ WDEBUG((WLOG_DEBUG, "wnet_sendfile_do: for %d, off=%ld, size=%d", e->fde_fd, (long) buf->wb_off, buf->wb_size));
 322+ /*
 323+ * On Solaris (sendfilev), FreeBSD, Tru64 UNIX and HP-UX (sendfile), we can write header data
 324+ * along with the sendfile, which improves performance and reduces syscall usage.
 325+ * At the moment this isn't supported, though...
 326+ *
 327+ * Linux sendfile() doesn't seem to have anything similar.
 328+ */
 329+#if defined __linux__ || defined __sun
 330+ i = sendfile(e->fde_fd, buf->wb_source, &buf->wb_off, buf->wb_size);
 331+#elif defined __FreeBSD__
 332+ i = sendfile(buf->wb_source, e->fde_fd, buf->wb_size, NULL, &off, 0);
 333+ buf->wb_off += off;
 334+#elif defined __hpux || (defined __digital__ && defined __unix__)
 335+ i = sendfile(e->fde_fd, buf->wb_source, buf->wb_off, buf->wb_size, NULL, 0);
 336+ buf->wb_off += i;
 337+#else
 338+# error i dont know how to invoke sendfile on this system
 339+#endif
 340+
 341+#ifdef __linux
 342+ /*
 343+ * The Linux sendfile() manual page says:
 344+ *
 345+ * When sendfile() returns, this variable will be set to the offset of the byte following the
 346+ * last byte that was read.
 347+ *
 348+ * However, this is not true on x86-64 when we are compiled as a 32-bit binary; the correct
 349+ * number of bytes is returned, but off is _not_ updated. So, we fudge it into working as we
 350+ * expect.
 351+ */
 352+ if (i > 0 && buf->wb_off == origoff)
 353+ buf->wb_off += i;
 354+#endif
 355+
 356+ buf->wb_size -= (buf->wb_off - origoff);
 357+ WDEBUG((WLOG_DEBUG, "sent %d bytes i=%d", (int)(buf->wb_off - origoff), i));
 358+
 359+ if (buf->wb_size == 0) {
 360+ wnet_register(e->fde_fd, FDE_WRITE, NULL, NULL);
 361+ buf->wb_func(e, buf->wb_udata, 0);
 362+ wfree(buf);
 363+ return;
 364+ }
 365+
 366+ if (i == -1 && errno != EWOULDBLOCK) {
 367+ wnet_register(e->fde_fd, FDE_WRITE, NULL, NULL);
 368+ buf->wb_func(e, buf->wb_udata, -1);
 369+ wfree(buf);
 370+ }
 371+
 372+ WDEBUG((WLOG_DEBUG, "wnet_sendfile_do: sendfile failed %s", strerror(errno)));
 373+
 374+ if (errno == EWOULDBLOCK)
 375+ return;
 376+
 377+}
 378+
 379+void
 380+wnet_set_time(void)
 381+{
 382+struct tm *now;
 383+ time_t old = current_time;
 384+ size_t n;
 385+
 386+ current_time = time(NULL);
 387+ if (current_time == old)
 388+ return;
 389+
 390+ now = gmtime(&current_time);
 391+
 392+ n = strftime(current_time_str, sizeof(current_time_str), "%a, %d %b %Y %H:%M:%S GMT", now);
 393+ assert(n);
 394+ n = strftime(current_time_short, sizeof(current_time_short), "%Y-%m-%d %H:%M:%S", now);
 395+ assert(n);
 396+}
 397+
 398+
 399+int
 400+readbuf_getdata(fde *fde)
 401+{
 402+ int i;
 403+
 404+ WDEBUG((WLOG_DEBUG, "readbuf_getdata: called"));
 405+ if (readbuf_data_left(&fde->fde_readbuf) == 0)
 406+ readbuf_reset(&fde->fde_readbuf);
 407+
 408+ if (readbuf_spare_size(&fde->fde_readbuf) == 0) {
 409+ WDEBUG((WLOG_DEBUG, "readbuf_getdata: no space in buffer"));
 410+ fde->fde_readbuf.rb_size += RDBUF_INC;
 411+ fde->fde_readbuf.rb_p = (char *)wrealloc(fde->fde_readbuf.rb_p, fde->fde_readbuf.rb_size);
 412+ }
 413+
 414+ if ((i = read(fde->fde_fd, readbuf_spare_start(&fde->fde_readbuf), readbuf_spare_size(&fde->fde_readbuf))) < 1)
 415+ return i;
 416+ fde->fde_readbuf.rb_dsize += i;
 417+ WDEBUG((WLOG_DEBUG, "readbuf_getdata: read %d bytes", i));
 418+
 419+ return i;
 420+}
 421+
 422+void
 423+readbuf_free(readbuf *buffer)
 424+{
 425+ if (buffer->rb_p)
 426+ free(buffer->rb_p);
 427+ bzero(buffer, sizeof(*buffer));
 428+}
 429+
 430+static void
 431+readbuf_reset(readbuf *buffer)
 432+{
 433+ buffer->rb_dpos = buffer->rb_dsize = 0;
 434+}
Index: trunk/willow/src/willow/wbackend.cc
@@ -0,0 +1,278 @@
 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 <cstdlib>
 19+#include <cstdio>
 20+#include <cstring>
 21+#include <cerrno>
 22+#include <climits>
 23+#include <cmath>
 24+#include <ctime>
 25+
 26+#include "willow.h"
 27+#include "wbackend.h"
 28+#include "wnet.h"
 29+#include "wlog.h"
 30+#include "confparse.h"
 31+#include "wconfig.h"
 32+
 33+#define rotl(i,r) (((i) << (r)) | ((i) >> (sizeof(i)*CHAR_BIT-(r))))
 34+
 35+vector<backend *> backends;
 36+
 37+static void backend_read(struct fde *);
 38+static struct backend *next_backend(string const &url);
 39+static uint32_t carp_urlhash(string const &);
 40+static uint32_t carp_hosthash(string const &);
 41+static uint32_t carp_combine(string const &, uint32_t);
 42+static void carp_recalc(string const &url);
 43+static void carp_calc(void);
 44+static int becarp_cmp(backend const *a, backend const *b);
 45+
 46+struct backend_cb_data {
 47+struct backend *bc_backend;
 48+ backend_cb bc_func;
 49+ void *bc_data;
 50+ string bc_url;
 51+};
 52+
 53+backend::backend(string const &name, string const &addr, int port)
 54+ : be_name(name)
 55+ , be_port(port)
 56+ , be_straddr(addr)
 57+ , be_dead(false)
 58+ , be_hash(carp_hosthash(be_name))
 59+ , be_load(1.)
 60+{
 61+ be_addr.sin_family = AF_INET;
 62+ be_addr.sin_port = htons(be_port);
 63+ be_addr.sin_addr.s_addr = inet_addr(be_name.c_str());
 64+}
 65+
 66+void
 67+add_backend(string const &addr, int port)
 68+{
 69+ backends.push_back(new backend(addr, addr, port));
 70+ carp_calc();
 71+ wlog(WLOG_NOTICE, "backend: %s:%d", addr.c_str(), port);
 72+}
 73+
 74+#if 0
 75+void
 76+backend_file(file)
 77+ char *file;
 78+{
 79+ FILE *f;
 80+ char line[1024];
 81+
 82+ if ((f = fopen(file, "r")) == NULL) {
 83+ perror(file);
 84+ exit(8);
 85+ }
 86+
 87+ while (fgets(line, sizeof line, f)) {
 88+ line[strlen(line) - 1] = '\0';
 89+ add_backend(line);
 90+ }
 91+
 92+ (void)fclose(f);
 93+}
 94+#endif
 95+
 96+int
 97+get_backend(string const &url, backend_cb func, void *data, int flags)
 98+{
 99+struct backend_cb_data *cbd;
 100+ int s;
 101+
 102+ WDEBUG((WLOG_DEBUG, "get_backend: called"));
 103+
 104+ cbd = new backend_cb_data;
 105+
 106+ cbd->bc_func = func;
 107+ cbd->bc_data = data;
 108+ cbd->bc_url = url;
 109+
 110+ for (;;) {
 111+ cbd->bc_backend = next_backend(url);
 112+
 113+ if (cbd->bc_backend == NULL) {
 114+ wfree(cbd);
 115+ return -1;
 116+ }
 117+
 118+ if ((s = wnet_open("backend connection")) == -1) {
 119+ wlog(WLOG_WARNING, "opening backend socket: %s", strerror(errno));
 120+ wfree(cbd);
 121+ return -1;
 122+ }
 123+
 124+ if (connect(s, (struct sockaddr *)&cbd->bc_backend->be_addr,
 125+ sizeof(cbd->bc_backend->be_addr)) == 0) {
 126+ WDEBUG((WLOG_DEBUG, "get_backend: connection completed immediately"));
 127+ func(cbd->bc_backend, &fde_table[s], data);
 128+ wfree(cbd);
 129+ return 0;
 130+ }
 131+
 132+ if (errno != EINPROGRESS) {
 133+ time_t retry = time(NULL) + config.backend_retry;
 134+ wnet_close(s);
 135+ wlog(WLOG_WARNING, "%s: %s; retry in %d seconds",
 136+ cbd->bc_backend->be_name.c_str(), strerror(errno), config.backend_retry);
 137+ cbd->bc_backend->be_dead = 1;
 138+ cbd->bc_backend->be_time = retry;
 139+ continue;
 140+ }
 141+
 142+ WDEBUG((WLOG_DEBUG, "get_backend: waiting for connection to complete"));
 143+ wnet_register(s, FDE_WRITE, backend_read, cbd);
 144+ return 0;
 145+ }
 146+}
 147+
 148+static void
 149+backend_read(fde *e)
 150+{
 151+struct backend_cb_data *cbd = static_cast<backend_cb_data *>(e->fde_rdata);
 152+ int error = 0;
 153+ socklen_t len = sizeof(error);
 154+
 155+ getsockopt(e->fde_fd, SOL_SOCKET, SO_ERROR, &error, &len);
 156+
 157+ if (error && error != EINPROGRESS) {
 158+ time_t retry = time(NULL) + config.backend_retry;
 159+ wnet_close(e->fde_fd);
 160+ wlog(WLOG_WARNING, "%s: [%d] %s; retry in %d seconds",
 161+ cbd->bc_backend->be_name.c_str(), error, strerror(error), config.backend_retry);
 162+ cbd->bc_backend->be_dead = 1;
 163+ cbd->bc_backend->be_time = time(NULL) + config.backend_retry;
 164+ if (get_backend(cbd->bc_url, cbd->bc_func, cbd->bc_data, 0) == -1) {
 165+ cbd->bc_func(NULL, NULL, cbd->bc_data);
 166+ }
 167+
 168+ wfree(cbd);
 169+ return;
 170+ }
 171+
 172+ /*
 173+ * After handing the fd off to the caller, we don't care about it
 174+ * any more.
 175+ */
 176+ wnet_register(e->fde_fd, FDE_WRITE, NULL, NULL);
 177+
 178+ cbd->bc_func(cbd->bc_backend, e, cbd->bc_data);
 179+ wfree(cbd);
 180+}
 181+
 182+static struct backend *
 183+next_backend(string const &url)
 184+{
 185+static size_t cur = 0;
 186+ size_t tried = 0;
 187+
 188+ if (config.use_carp)
 189+ carp_recalc(url);
 190+
 191+ WDEBUG((WLOG_DEBUG, "next_backend: url=[%s]", url.c_str()));
 192+
 193+ while (tried++ <= backends.size()) {
 194+ time_t now = time(NULL);
 195+
 196+ if (cur >= backends.size())
 197+ cur = 0;
 198+
 199+ if (backends[cur]->be_dead && now >= backends[cur]->be_time)
 200+ backends[cur]->be_dead = 0;
 201+
 202+ if (backends[cur]->be_dead) {
 203+ cur++;
 204+ continue;
 205+ }
 206+
 207+ if (config.use_carp)
 208+ cur = 0;
 209+ return backends[cur++];
 210+ }
 211+
 212+ return NULL;
 213+}
 214+
 215+static uint32_t
 216+carp_urlhash(string const &str)
 217+{
 218+ uint32_t h = 0;
 219+ for (string::const_iterator it = str.begin(), end = str.end(); it != end; ++it)
 220+ h += rotl(h, 19) + *it;
 221+ return h;
 222+}
 223+
 224+static uint32_t
 225+carp_hosthash(string const &str)
 226+{
 227+ uint32_t h = carp_urlhash(str) * 0x62531965;
 228+ return rotl(h, 21);
 229+}
 230+
 231+static uint32_t
 232+carp_combine(string const &url, uint32_t host)
 233+{
 234+ uint32_t c = carp_urlhash(url) ^ host;
 235+ c += c * 0x62531965;
 236+ return rotl(c, 21);
 237+}
 238+
 239+static void
 240+carp_calc(void)
 241+{
 242+struct backend *be, *prev;
 243+ size_t i, j;
 244+
 245+ backends[0]->be_carp = (uint32_t) pow((backends.size() * backends[0]->be_load), 1.0 / backends.size());
 246+ backends[0]->be_carplfm = 1.0;
 247+ for (i = 1; i < backends.size(); ++i) {
 248+ float l = 0;
 249+ be = backends[i];
 250+ prev = backends[i - 1];
 251+ be->be_carplfm = 1.0 + ((backends.size()-i+1) * (be->be_load - prev->be_load));
 252+ for (j = 0; j < i; ++j)
 253+ l *= backends[j]->be_carp;
 254+ be->be_carp = (uint32_t) (be->be_carp / l);
 255+ be->be_carp += (uint32_t) pow(prev->be_carp, backends.size()-i+1);
 256+ be->be_carp = (uint32_t) pow(be->be_carp, 1/(backends.size()-i+1));
 257+ }
 258+}
 259+
 260+static void
 261+carp_recalc(string const &url)
 262+{
 263+ uint32_t hash;
 264+ size_t i;
 265+ for (i = 0; i < backends.size(); ++i) {
 266+ hash = carp_urlhash(url) ^ backends[i]->be_hash;
 267+ hash += hash * 0x62531965;
 268+ hash = rotl(hash, 21);
 269+ hash *= (uint32_t) backends[i]->be_carplfm;
 270+ backends[i]->be_carp = hash;
 271+ }
 272+ sort(backends.begin(), backends.end(), becarp_cmp);
 273+}
 274+
 275+static int
 276+becarp_cmp(backend const *a, backend const *b)
 277+{
 278+ return a->be_carp - b->be_carp;
 279+}
Index: trunk/willow/src/willow/parser.y
@@ -1,38 +1,35 @@
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.
 2+/* This code is in the public domain.
 3+ * $Nightmare: nightmare/src/main/parser.y,v 1.2.2.1.2.1 2002/07/02 03:42:10 ejb Exp $
 4+ * $Header$
95 */
106
117 %{
12 -#if defined __SUNPRO_CC || defined __DECC || defined __HP_cc
13 -# pragma ident "@(#)$Header$"
14 -#endif
15 -
168 #include <sys/types.h>
179 #include <sys/stat.h>
1810
1911 #include <netinet/in.h>
 12+#include <arpa/inet.h>
 13+#include <netdb.h>
2014
21 -#include <string.h>
22 -#include <stdlib.h>
23 -#include <stdarg.h>
24 -#include <stdio.h>
 15+#include <vector>
 16+using std::vector;
2517
 18+#include <cstdlib>
 19+#include <cstdarg>
 20+#include <cstdio>
 21+
 22+#define NEED_PARSING_TREE
2623 #include "willow.h"
2724 #include "confparse.h"
2825
2926 #define YY_NO_UNPUT
3027
31 -int yyparse();
32 -int yyerror(const char *);
33 -int yylex();
 28+/* icc emits these with -w2 */
 29+#ifdef __INTEL_COMPILER
 30+# pragma warning (disable : 193)
 31+#endif
3432
35 -static time_t conf_find_time(const char*);
36 -static void add_cur_list(int, const char *, int);
 33+static time_t conf_find_time(string const &);
3734
3835 static struct {
3936 const char * name;
@@ -59,24 +56,130 @@
6057 {NULL, NULL, 0},
6158 };
6259
63 -static time_t
64 -conf_find_time(name)
65 - const char *name;
 60+time_t conf_find_time(string const &name)
6661 {
67 - int i;
 62+ int i;
6863
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;
 64+ for (i = 0; conf_times[i].name; i++)
 65+ {
 66+ if (conf_times[i].name ==name ||
 67+ (conf_times[i].plural && conf_times[i].plural == name))
 68+ return conf_times[i].val;
 69+ }
 70+
 71+ return 0;
 72+}
 73+
 74+/*ARGSUSED*/
 75+static conf::value *
 76+f_hostname(vector<conf::avalue> *args)
 77+{
 78+#ifndef HOST_NAME_MAX
 79+# define HOST_NAME_MAX 255 /* SUSv2 */
 80+#endif
 81+char host[HOST_NAME_MAX] = { 0 };
 82+conf::value *ret;
 83+conf::avalue aval;
 84+ gethostname(host, sizeof(host));
 85+ ret = new conf::value(conf::declpos::here());
 86+ aval.av_type = conf::cv_qstring;
 87+ aval.av_strval = host;
 88+ ret->cv_values.push_back(aval);
 89+ return ret;
 90+}
 91+
 92+static conf::value *
 93+f_dns(vector<conf::avalue> *args)
 94+{
 95+struct addrinfo *res, hints;
 96+string aftype;
 97+conf::value *ret;
 98+int i;
 99+conf::avalue aval;
 100+char tmp[64];
 101+ ret = new conf::value(conf::declpos::here());
 102+ aftype = (*args)[0].av_strval.c_str();
 103+ bzero(&hints, sizeof(hints));
 104+ if (aftype == "ipv4")
 105+ hints.ai_family = AF_INET;
 106+ else if (aftype == "ipv6")
 107+ hints.ai_family = AF_INET6;
 108+ else {
 109+ return ret;
73110 }
 111+ if ((i = getaddrinfo((*args)[0].av_strval.c_str(), "80", &hints, &res)) != 0) {
 112+ conf::report_parse_error("getaddrinfo(%s): %s",
 113+ (*args)[0].av_strval.c_str(), gai_strerror(i));
 114+ return ret;
 115+ }
 116+
 117+ /* format the address as an IP */
 118+ aval.av_type = conf::cv_qstring;
 119+ inet_ntop(res->ai_family, res->ai_addr->sa_data, tmp, sizeof(tmp));
 120+ aval.av_strval = tmp;
 121+ ret->cv_values.push_back(aval);
 122+ freeaddrinfo(res);
 123+ return ret;
 124+}
74125
75 - return 0;
 126+typedef struct function_stru {
 127+ const char *name;
 128+ conf::value *(*execute)(vector<conf::avalue> *args);
 129+ int args[3]; /* XXX */
 130+} function_t;
 131+
 132+static function_t functions[] = {
 133+ { "hostname", f_hostname, { 0, 0, 0 } },
 134+ { "dns", f_dns, { conf::cv_qstring, conf::cv_string, 0 } },
 135+ { NULL, NULL, { } }
 136+};
 137+
 138+static int
 139+match_func_parms(function_t *f, vector<conf::avalue> *args)
 140+{
 141+size_t i;
 142+vector<conf::avalue>::const_iterator it, end;
 143+ it = args->begin();
 144+ end = args->end();
 145+ for (i = 0; f->args[i]; ++i) {
 146+ if (i+1 > args->size()) {
 147+ conf::report_parse_error("not enough arguments to function '%s' (got %d)",
 148+ f->name, i+1);
 149+ return 0;
 150+ }
 151+ if (f->args[i] != (it)->av_type) {
 152+ conf::report_parse_error("wrong type %d for argument %d to '%s' (expected %d)",
 153+ (it)->av_type, i + 1, f->name, f->args[i]);
 154+ return 0;
 155+ }
 156+ it++;
 157+ }
 158+ if (args->size() > i) {
 159+ conf::report_parse_error("too many arguments to function '%s'", f->name);
 160+ return 0;
 161+ }
 162+ return 1;
76163 }
77164
78 -static struct {
79 -const char *word;
80 - int yesno;
 165+static function_t*
 166+find_function(string const &name, vector<conf::avalue> *args)
 167+{
 168+function_t *f;
 169+ for (f = functions; f->name; ++f)
 170+ if (f->name == name)
 171+ {
 172+ if (match_func_parms(f, args))
 173+ return f;
 174+ else
 175+ break;
 176+ }
 177+ return NULL;
 178+}
 179+
 180+static struct
 181+{
 182+ const char *word;
 183+ int yesno;
81184 } yesno[] = {
82185 {"yes", 1},
83186 {"no", 0},
@@ -88,230 +191,349 @@
89192 };
90193
91194 static int
92 -conf_get_yesno_value(str)
93 - const char *str;
 195+conf_get_yesno_value(string const &str)
94196 {
95 - int i;
96 -
 197+int i;
97198 for (i = 0; yesno[i].word; i++)
98 - if (!strcasecmp(str, yesno[i].word))
 199+ if (str == yesno[i].word)
99200 return yesno[i].yesno;
100201
101202 return -1;
102203 }
103204
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 -
167205 %}
168206
169207 %union {
170 - int number;
171 - char *string;
172 - conf_parm_t *conf_parm;
 208+ long number;
 209+ string *string_;
 210+ conf::avalue *avalue;
 211+ conf::value *value;
 212+ vector<conf::value> *value_list;
 213+ vector<conf::avalue> *avalue_list;
 214+ vector<conf::item_entry>*entry_list;
 215+ conf::item_entry *entry;
 216+ bool bool_;
173217 }
174218
175 -%token TWODOTS
 219+%token TWODOTS VAR TEMPLATE FROM
176220
177 -%token <string> QSTRING STRING
 221+%token <string_> QSTRING STRING VARNAME
178222 %token <number> NUMBER
179223
180 -%type <string> qstring string
181 -%type <number> number timespec
182 -%type <conf_parm> oneitem single itemlist
 224+%type <string_> qstring string varname astring from_clause key_clause
 225+%type <number> number timespec
 226+%type <avalue> oneitem
 227+%type <avalue_list> single
 228+%type <avalue_list> itemlist
 229+%type <entry_list> block_items optional_block
 230+%type <entry> block_item
 231+%type <bool_> template_clause
 232+%type <avalue_list> func_args
 233+%type <value> function
183234
 235+%left '+'
 236+%nonassoc poneitem
 237+%nonassoc ptimespec
184238 %start conf
185239
186240 %%
187241
188242 conf:
189243 | conf conf_item
190 - | error
191244 ;
192245
193246 conf_item: block
194247 ;
195248
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 - ;
 249+from_clause:
 250+ { $$ = NULL; }
 251+ | FROM astring {
 252+ $$ = $2;
 253+ }
 254+ ;
 255+key_clause:
 256+ { $$ = NULL; }
 257+ | astring {
 258+ $$ = $1;
 259+ }
 260+ ;
218261
219 -block_items: block_items block_item
220 - | block_item
221 - ;
 262+template_clause:
 263+ { $$ = 0; }
 264+ | TEMPLATE { $$ = 1; }
 265+ ;
222266
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);
 267+semicolon:
 268+ ';'
 269+ | { conf::report_parse_error("expected ';'"); }
 270+ ;
 271+
 272+equals:
 273+ '='
 274+ | { conf::report_parse_error("expected '='"); }
 275+ ;
 276+
 277+func_args:
 278+ { $$ = new vector<conf::avalue>; }
 279+ | itemlist {
 280+ $$ = $1;
 281+ delete $1;
 282+ }
 283+ ;
 284+
 285+function:
 286+ string '(' func_args ')'
 287+ {
 288+ function_t *func;
 289+ if ((func = find_function(*$1, $3)) == NULL) {
 290+ conf::report_parse_error("undefined function %s", $1);
 291+ $$ = new conf::value(conf::declpos::here());
 292+ } else {
 293+ $$ = func->execute($3);
229294 }
230 - ;
 295+ free($1);
 296+ }
 297+ ;
231298
 299+optional_block:
 300+ {
 301+ $$ = NULL;
 302+ }
 303+ | '{' block_items '}'
 304+ {
 305+ $$ = $2;
 306+ }
 307+ ;
 308+
 309+block: template_clause string key_clause from_clause optional_block semicolon
 310+ {
 311+ const char *block_key;
 312+ char nname[10];
 313+ static int nseq;
 314+ bool unnamed = false;
 315+ conf::tree_entry *e;
 316+ vector<conf::item_entry>::const_iterator it, end;
 317+ if ($3)
 318+ block_key = $3->c_str();
 319+ else {
 320+ sprintf(nname, "__%d", nseq++);
 321+ block_key = nname;
 322+ unnamed = true;
 323+ }
 324+ if ($4) {
 325+ if ((e = conf::new_tree_entry_from_template(conf::parsing_tree, *$2, block_key, *$4,
 326+ conf::declpos::here(), unnamed, $1)) == NULL) {
 327+ conf::report_parse_error("template block \"%s\" not found", $4);
 328+ goto end;
 329+ }
 330+ } else {
 331+ if ((e = conf::parsing_tree.find(*$2, block_key)) != NULL) {
 332+ conf::report_parse_error("%s \"%s\" already defined at %s",
 333+ $2->c_str(), block_key, e->item_pos.format().c_str());
 334+ goto end;
 335+ }
 336+ e = conf::parsing_tree.find_or_new(*$2, block_key, conf::declpos::here(), unnamed, $1);
 337+ }
 338+
 339+ if ($5) for (it = $5->begin(), end = $5->end(); it != end; ++it) {
 340+ e->add(*it->val);
 341+ delete it->val;
 342+ }
 343+ end:
 344+ delete $2;
 345+ delete $3;
 346+ delete $4;
 347+ }
 348+ | VAR varname equals itemlist semicolon
 349+ {
 350+ conf::value *value;
 351+ value = new conf::value(conf::declpos::here());
 352+ value->cv_name = $2->substr(1);
 353+ value->cv_values = *$4;
 354+ conf::add_variable(value);
 355+ delete $4;
 356+ }
 357+ | error
 358+ ;
 359+
 360+block_items:
 361+ block_items block_item
 362+ {
 363+ $$ = new vector<conf::item_entry>($1->begin(), $1->end());
 364+ $$->push_back(*$2);
 365+ delete $1;
 366+ delete $2;
 367+ }
 368+ | block_item
 369+ {
 370+ $$ = new vector<conf::item_entry>;
 371+ $$->push_back(*$1);
 372+ delete $1;
 373+ }
 374+ ;
 375+
 376+block_item:
 377+ string equals itemlist semicolon
 378+ {
 379+ conf::value *val = new conf::value(conf::declpos::here());
 380+ $$ = new conf::item_entry(conf::declpos::here(), val);
 381+ $$->val->cv_name = *$1;
 382+ $$->val->cv_values = *$3;
 383+ }
 384+ ;
 385+
 386+/*
 387+ * "single" is a list of items.
 388+ */
232389 itemlist: itemlist ',' single
 390+ {
 391+ $$->insert($$->end(), $3->begin(), $3->end());
 392+ delete $3;
 393+ delete $1;
 394+ }
233395 | single
 396+ {
 397+ $$ = new vector<conf::avalue>;
 398+ $$->insert($$->end(), $1->begin(), $1->end());
 399+ delete $1;
 400+ }
234401 ;
235402
236403 single: oneitem
237404 {
238 - add_cur_list_cpt($1);
 405+ $$ = new vector<conf::avalue>;
 406+ $$->push_back(*$1);
 407+ delete $1;
239408 }
240409 | oneitem TWODOTS oneitem
241410 {
 411+ $$ = new vector<conf::avalue>;
242412 /* "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.");
 413+ if ($1->av_type != conf::cv_int || $3->av_type != conf::cv_int) {
 414+ conf::report_parse_error("both arguments in '..' notation must be integers.");
246415 break;
247416 } else {
248 - int i;
249 -
250 - for (i = $1->v.number; i <= $3->v.number; i++) {
251 - add_cur_list(CF_INT, 0, i);
 417+ int i;
 418+ conf::avalue val;
 419+ for (i = $1->av_intval; i <= $3->av_intval; i++) {
 420+ val.av_type = conf::cv_int;
 421+ val.av_intval = i;
 422+ $$->push_back(val);
252423 }
253424 }
 425+ delete $1;
 426+ delete $3;
254427 }
 428+ | varname
 429+ {
 430+ string varname_;
 431+ conf::value *value;
 432+ $$ = new vector<conf::avalue>;
 433+ varname_ = $1->substr(1);
 434+ value = conf::value_from_variable("", varname_, conf::declpos::here());
 435+ if (value == NULL) {
 436+ conf::report_parse_error("undefined variable %s", varname_.c_str());
 437+ } else {
 438+ $$->insert($$->begin(), value->cv_values.begin(), value->cv_values.end());
 439+ }
 440+ }
 441+ | single '+' single
 442+ {
 443+ int flen, slen;
 444+ flen = $1->size();
 445+ slen = $3->size();
 446+ if (flen == 1 && slen == 1) {
 447+ conf::avalue n;
 448+ n.av_type = conf::cv_qstring;
 449+ n.av_strval = (*$1)[0].av_strval + (*$3)[0].av_strval;
 450+ $$->push_back(n);
 451+ } else {
 452+ conf::report_parse_error("do not know how to add these values (%d+%d)", flen, slen);
 453+ }
 454+ }
255455 ;
256456
257 -oneitem: qstring
 457+oneitem: astring
258458 {
259 - $$ = wmalloc(sizeof(conf_parm_t));
260 - $$->type = CF_QSTRING;
261 - $$->v.string = wstrdup($1);
262 - wfree($1);
 459+ $$ = new conf::avalue;
 460+ $$->av_type = conf::cv_qstring;
 461+ $$->av_strval = *$1;
 462+ delete $1;
263463 }
264 - | timespec
 464+ | timespec
265465 {
266 - $$ = wmalloc(sizeof(conf_parm_t));
267 - $$->type = CF_TIME;
268 - $$->v.number = $1;
 466+ $$ = new conf::avalue;
 467+ $$->av_type = conf::cv_time;
 468+ $$->av_intval = $1;
269469 }
270470 | number
271471 {
272 - $$ = wmalloc(sizeof(conf_parm_t));
273 - $$->type = CF_INT;
274 - $$->v.number = $1;
 472+ $$ = new conf::avalue;
 473+ $$->av_type = conf::cv_int;
 474+ $$->av_intval = $1;
275475 }
276476 | string
277477 {
278478 /* a 'string' could also be a yes/no value ..
279479 so pass it as that, if so */
280 - int val = conf_get_yesno_value($1);
 480+ int val = conf_get_yesno_value(*$1);
281481
282 - $$ = wmalloc(sizeof(conf_parm_t));
 482+ $$ = new conf::avalue;
283483
284484 if (val != -1) {
285 - $$->type = CF_YESNO;
286 - $$->v.number = val;
 485+ $$->av_type = conf::cv_yesno;
 486+ $$->av_intval = val;
287487 } else {
288 - $$->type = CF_STRING;
289 - $$->v.string = wstrdup($1);
 488+ $$->av_type = conf::cv_string;
 489+ $$->av_strval = *$1;
290490 }
291 - wfree($1);
 491+ delete $1;
292492 }
293493 ;
294494
295 -qstring: QSTRING { strcpy($$, $1); } ;
296 -string: STRING { strcpy($$, $1); } ;
 495+astring:
 496+ qstring {
 497+ $$ = $1;
 498+ }
 499+ | function {
 500+ conf::value *value;
 501+ value = $1;
 502+ if (!value->is_single(conf::cv_qstring)) {
 503+ value->report_error("function in concatenation must return quoted string");
 504+ $$ = new string("");
 505+ } else {
 506+ $$ = new string($1->cv_values[0].av_strval);
 507+ }
 508+ delete $1;
 509+ }
 510+ ;
 511+
 512+qstring:
 513+ QSTRING {
 514+ $$ = $1;
 515+ }
 516+ | QSTRING QSTRING {
 517+ $$ = new string (*$1 + *$2);
 518+ delete $1;
 519+ delete $2;
 520+ }
 521+ ;
 522+string: STRING { $$ = $1; } ;
297523 number: NUMBER { $$ = $1; } ;
 524+varname: VARNAME { $$ = $1; } ;
298525
299 -timespec: number string
 526+timespec: number string
300527 {
301 - time_t t;
302 -
303 - if ((t = conf_find_time($2)) == 0) {
304 - conf_report_error("Unrecognised time type/size '%s'", $2);
 528+ time_t t;
 529+ if ((t = conf_find_time(*$2)) == 0) {
 530+ conf::report_parse_error("unrecognised time type/size \"%s\"", $2->c_str());
305531 t = 1;
306532 }
307 -
 533+ delete $2;
308534 $$ = $1 * t;
309535 }
310536 | timespec timespec
311537 {
312538 $$ = $1 + $2;
313539 }
314 - | timespec number
315 - {
316 - $$ = $1 + $2;
317 - }
318540 ;
Index: trunk/willow/src/willow/confparse.cc
@@ -0,0 +1,549 @@
 2+/* This code is in the public domain.
 3+ * $Id$
 4+ */
 5+
 6+#include <vector>
 7+#include <string>
 8+#include <utility>
 9+#include <cstdarg>
 10+#include <cerrno>
 11+using std::string;
 12+using std::vector;
 13+using std::make_pair;
 14+
 15+#define NEED_PARSING_TREE
 16+
 17+#include "confparse.h"
 18+#include "willow.h"
 19+#include "wlog.h"
 20+#include "wbackend.h"
 21+#include "wconfig.h"
 22+
 23+namespace conf {
 24+
 25+tree global_conf_tree;
 26+map<string, value> variable_list;
 27+
 28+vector<string> ignorables;
 29+static void add_ignorable(string const &);
 30+static int is_ignorable(string const &);
 31+int parse_error;
 32+
 33+int curpos = 0;
 34+int lineno = 0;
 35+string linebuf;
 36+string current_file;
 37+tree parsing_tree;
 38+
 39+const int require_name = 0x1;
 40+
 41+tree *
 42+parse_file(string const &file)
 43+{
 44+ parsing_tree.reset();
 45+ if ((yyin = fopen(file.c_str(), "r")) == NULL) {
 46+ wlog(WLOG_ERROR, "could not open configuration file %s: %s",
 47+ file.c_str(), strerror(errno));
 48+ return NULL;
 49+ }
 50+ if (yyparse() || parse_error)
 51+ return NULL;
 52+ return &parsing_tree;
 53+}
 54+
 55+void
 56+free_newconf_state(void)
 57+{
 58+ variable_list.clear();
 59+}
 60+
 61+void
 62+add_variable(value *val)
 63+{
 64+map<string, value>::iterator it;
 65+ it = variable_list.find(val->cv_name);
 66+ if (it != variable_list.end())
 67+ return;
 68+ variable_list.insert(make_pair(val->cv_name, *val));
 69+}
 70+
 71+void
 72+add_variable_simple(string const &name, string const &vval)
 73+{
 74+value var = declpos();
 75+avalue aval;
 76+ var.cv_name = name;
 77+ aval.av_type = cv_qstring;
 78+ aval.av_strval = vval;
 79+ var.cv_values.push_back(aval);
 80+ variable_list.insert(make_pair(var.cv_name, var));
 81+}
 82+
 83+value *
 84+value_from_variable(string const &name, string const &varname, declpos const &pos)
 85+{
 86+map<string, value>::iterator it;
 87+value *val;
 88+ it = variable_list.find(varname);
 89+ if (it == variable_list.end())
 90+ return NULL;
 91+ val = new value(it->second);
 92+ val->cv_pos = pos;
 93+ return val;
 94+}
 95+
 96+
 97+static void
 98+add_ignorable(std::string const &pat)
 99+{
 100+ ignorables.push_back(pat);
 101+}
 102+
 103+static int
 104+is_ignorable(std::string const &pat)
 105+{
 106+ return std::find(ignorables.begin(), ignorables.end(), pat) != ignorables.end();
 107+}
 108+
 109+
 110+struct if_entry {
 111+ const char *name;
 112+ bool true_;
 113+} if_table[] = {
 114+ { NULL, false }
 115+};
 116+
 117+bool
 118+if_true(std::string const &if_)
 119+{
 120+if_entry *e;
 121+char const *dir, *od;
 122+ dir = od = if_.c_str();
 123+ dir += sizeof("%if");
 124+ while (isspace(*dir))
 125+ dir++;
 126+
 127+ for (e = if_table; e->name; ++e)
 128+ if (e->name == dir)
 129+ return e->true_;
 130+ conf::report_parse_error("unknown %%if directive \"%s\"", dir);
 131+ return false;
 132+}
 133+
 134+void
 135+tree_entry::add(value const &v)
 136+{
 137+value *existing;
 138+ if ((existing = (*this)/v.cv_name) != NULL) {
 139+ existing->cv_values.insert(
 140+ existing->cv_values.end(),
 141+ v.cv_values.begin(), v.cv_values.end());
 142+ return;
 143+ }
 144+ item_values.insert(make_pair(v.cv_name, v));
 145+}
 146+
 147+tree_entry *
 148+tree::find(string const &block, string const &name)
 149+{
 150+vector<tree_entry>::iterator it, end;
 151+ for (it = entries.begin(), end = entries.end(); it != end; ++it) {
 152+ if (it->item_name == block && (name.empty() || (name == it->item_key))) {
 153+ it->item_touched = true;
 154+ return &*it;
 155+ }
 156+ }
 157+ return NULL;
 158+}
 159+
 160+tree_entry *
 161+tree::find(string const &block)
 162+{
 163+ return find(block, "");
 164+}
 165+
 166+tree_entry *
 167+tree::find_or_new(
 168+ string const &block,
 169+ string const &name,
 170+ declpos const &pos,
 171+ bool unnamed,
 172+ bool is_template
 173+) {
 174+tree_entry *f, n(pos);
 175+ if ((f = find(block, name)) != NULL)
 176+ return f;
 177+
 178+ n.item_unnamed = unnamed;
 179+ n.item_name = block;
 180+ n.item_key = name;
 181+ n.item_pos = pos;
 182+ n.item_is_template = is_template;
 183+ entries.push_back(n);
 184+ return &*entries.rbegin();
 185+}
 186+
 187+tree_entry *
 188+tree::find_item(tree_entry const &e)
 189+{
 190+ if (e.item_unnamed)
 191+ return find(e.item_name);
 192+ else return find(e.item_name, e.item_key);
 193+}
 194+
 195+tree_entry *
 196+new_tree_entry_from_template(
 197+ tree &t,
 198+ string const &block,
 199+ string const &name,
 200+ string const &templatename,
 201+ declpos const &pos,
 202+ bool unnamed,
 203+ bool is_template
 204+) {
 205+tree_entry *n, *e;
 206+value *value;
 207+void *pp = NULL;
 208+const char *key;
 209+ if ((e = t.find(block, templatename)) == NULL)
 210+ return e;
 211+ n = t.find_or_new(block, name, pos, unnamed, true);
 212+ n->item_values = e->item_values;
 213+ return n;
 214+}
 215+
 216+
 217+int
 218+find_untouched(tree &t)
 219+{
 220+int i = 0;
 221+const char *key;
 222+value *val;
 223+void *pp = NULL;
 224+vector<tree_entry>::const_iterator it, end;
 225+map<string, value>::const_iterator vit, vend;
 226+ for (it = t.entries.begin(), end = t.entries.end(); it != end; ++it) {
 227+ if (it->item_is_template)
 228+ continue;
 229+ if (!it->item_touched) {
 230+ it->report_error("top-level block \"%s\" not recognised", it->item_name.c_str());
 231+ i++;
 232+ continue;
 233+ }
 234+ for (vit = it->item_values.begin(), vend = it->item_values.end(); vit != vend; ++vit) {
 235+ string name;
 236+ if (vit->second.cv_touched)
 237+ continue;
 238+ if (!it->item_unnamed)
 239+ name = "/" + it->item_name + "=" + it->item_key + "/" + vit->second.cv_name;
 240+ else
 241+ name = "/" + it->item_name + "/" + vit->second.cv_name;
 242+ if (is_ignorable(name))
 243+ continue;
 244+ vit->second.report_error("%s was not recognised", name.c_str());
 245+ i++;
 246+ }
 247+ }
 248+ return i;
 249+}
 250+
 251+value *
 252+tree_entry::operator/(string const &name)
 253+{
 254+map<string, value>::iterator it;
 255+ it = item_values.find(name);
 256+ if (it == item_values.end())
 257+ return NULL;
 258+ it->second.cv_touched = true;
 259+ return &it->second;
 260+}
 261+
 262+/*
 263+ * add a new top-level item to the tree.
 264+ */
 265+bool
 266+tree::add(tree_entry const &item)
 267+{
 268+value *existing;
 269+ /* if the entry already exists, do nothing */
 270+ if (find_item(item) != NULL)
 271+ return false;
 272+ entries.push_back(item);
 273+ return true;
 274+}
 275+
 276+void
 277+handle_pragma(string const &pragma)
 278+{
 279+char *mp, *op, *ap;
 280+ mp = wstrdup(pragma.c_str());
 281+ op = mp;
 282+ if (*mp == '\n')
 283+ ++mp;
 284+ /* skip '%pragma ' */
 285+ mp += sizeof("%pragma");
 286+ while (isspace(*mp))
 287+ ++mp;
 288+
 289+ /* now up to the first space or EOS is the pragma name */
 290+ if ((ap = strchr(mp, ' ')) != NULL)
 291+ *ap++ = '\0';
 292+ if (!strcmp(mp, "ignore_ok")) {
 293+ if (*ap != '"' || ap[strlen(ap) - 1] != '"')
 294+ report_parse_error("%%pragma ignore_ok must be followed by a quoted string");
 295+ else {
 296+ const char *sp;
 297+ ap++;
 298+ ap[strlen(ap) - 1] = '\0';
 299+ for (sp = ap; *sp; ++sp) {
 300+ if (!(isalnum(*sp) || strchr("_?*/=", *sp)))
 301+ report_parse_error("\"%s\" is not a valid value mask", ap);
 302+ else {
 303+ add_ignorable(ap);
 304+ }
 305+ }
 306+ }
 307+ } else {
 308+ report_parse_error("unrecognised %%pragma \"%s\"", mp);
 309+ }
 310+
 311+ wfree(op);
 312+}
 313+
 314+value::value(declpos const &pos)
 315+ : cv_touched(false)
 316+ , cv_pos(pos)
 317+{
 318+}
 319+
 320+tree_entry::tree_entry(declpos const &pos)
 321+ : item_pos(pos)
 322+ , item_unnamed(false)
 323+ , item_touched(false)
 324+ , item_is_template(false)
 325+{
 326+}
 327+
 328+avalue::avalue()
 329+ : av_intval(0)
 330+ , av_type(0)
 331+{
 332+}
 333+
 334+/* public functions */
 335+
 336+void
 337+report_parse_error(const char *fmt, ...)
 338+{
 339+va_list ap;
 340+char msg[1024] = { 0 };
 341+
 342+ parse_error = 1;
 343+
 344+ va_start(ap, fmt);
 345+ vsnprintf(msg, sizeof msg, fmt, ap);
 346+ va_end(ap);
 347+
 348+ wlog(WLOG_ERROR, "\"%s\", line %d: %s", current_file.c_str(), lineno, msg);
 349+}
 350+
 351+void
 352+value::vreport_error(const char *fmt, va_list ap) const
 353+{
 354+char msg[1024] = { 0 };
 355+ vsnprintf(msg, sizeof msg, fmt, ap);
 356+ wlog(WLOG_ERROR, "%s: %s", cv_pos.format().c_str(), msg);
 357+}
 358+
 359+void
 360+value::report_error(const char *fmt, ...) const
 361+{
 362+va_list ap;
 363+ va_start(ap, fmt);
 364+ vreport_error(fmt, ap);
 365+ va_end(ap);
 366+}
 367+
 368+bool
 369+value::is_single(cv_type t) const
 370+{
 371+ return nvalues() == 1 && cv_values[0].av_type == t;
 372+}
 373+
 374+size_t
 375+value::nvalues(void) const
 376+{
 377+ return cv_values.size();
 378+}
 379+
 380+void
 381+tree_entry::vreport_error(const char *fmt, va_list ap) const
 382+{
 383+char msg[1024] = { 0 };
 384+ vsnprintf(msg, sizeof msg, fmt, ap);
 385+ wlog(WLOG_ERROR, "%s: %s", item_pos.format().c_str(), msg);
 386+}
 387+
 388+void
 389+tree_entry::report_error(const char *fmt, ...) const
 390+{
 391+va_list ap;
 392+ va_start(ap, fmt);
 393+ vreport_error(fmt, ap);
 394+ va_end(ap);
 395+}
 396+
 397+void
 398+catastrophic_error(const char *fmt, ...)
 399+{
 400+char msg[1024] = { 0 };
 401+va_list ap;
 402+ va_start(ap, fmt);
 403+ vsnprintf(msg, sizeof msg, fmt, ap);
 404+ va_end(ap);
 405+ wlog(WLOG_ERROR, "\"%s\", line %d: catastrophic error: %s", current_file.c_str(), lineno, msg);
 406+}
 407+
 408+extern "C" void
 409+yyerror(const char *err)
 410+{
 411+ wlog(WLOG_ERROR, "\"%s\", line %d: %s", current_file.c_str(), lineno, err);
 412+}
 413+
 414+void
 415+tree::reset(void)
 416+{
 417+ entries.clear();
 418+}
 419+
 420+bool
 421+find_include(string &file)
 422+{
 423+ return false;
 424+}
 425+
 426+block_definer &
 427+conf_definer::block(string const &name, int flags)
 428+{
 429+block_definer *b = new block_definer(*this, name, flags);
 430+ blocks.push_back(b);
 431+ return *b;
 432+}
 433+
 434+bool
 435+value_definer::validate(tree_entry &e, value &v)
 436+{
 437+ return (*vv)(e, v);
 438+}
 439+
 440+void
 441+value_definer::set(tree_entry &e, value &v)
 442+{
 443+ (*vs)(e, v);
 444+}
 445+
 446+block_definer::block_definer(conf_definer &parent_, string const &name_, int flags_)
 447+ : parent(parent_)
 448+ , name(name_)
 449+ , vefn(NULL)
 450+ , sefn(NULL)
 451+ , flags(flags_)
 452+{
 453+}
 454+
 455+block_definer &
 456+block_definer::block(string const &name, int flags)
 457+{
 458+ return parent.block(name, flags);
 459+}
 460+
 461+bool
 462+block_definer::validate(tree_entry &e)
 463+{
 464+map<string, conf::value>::iterator it, end;
 465+map<string, value_definer *>::iterator vit;
 466+bool ret = true;
 467+ for (it = e.item_values.begin(), end = e.item_values.end(); it != end; ++it) {
 468+ vit = values.find(it->first);
 469+ if (vit == values.end())
 470+ continue;
 471+ it->second.cv_touched = true;
 472+ ret = vit->second->validate(e, it->second) && ret;
 473+ }
 474+ if (vefn)
 475+ ret = (*vefn)(e) && ret;
 476+ if ((flags & require_name) && e.item_key.empty()) {
 477+ e.report_error("this block cannot be unnamed");
 478+ ret = false;
 479+ }
 480+ return ret;
 481+}
 482+
 483+void
 484+block_definer::set(tree_entry &e)
 485+{
 486+map<string, conf::value>::iterator it, end;
 487+map<string, value_definer *>::iterator vit;
 488+ for (it = e.item_values.begin(), end = e.item_values.end(); it != end; ++it) {
 489+ vit = values.find(it->first);
 490+ if (vit == values.end())
 491+ continue;
 492+ vit->second->set(e, it->second);
 493+ }
 494+ if (sefn)
 495+ (*sefn)(e);
 496+}
 497+
 498+string
 499+type_name(cv_type t)
 500+{
 501+ switch (t) {
 502+ case cv_string:
 503+ return "string";
 504+ case cv_qstring:
 505+ return "quoted string";
 506+ case cv_yesno:
 507+ return "boolean";
 508+ case cv_int:
 509+ return "integer";
 510+ case cv_time:
 511+ return "time or size";
 512+ default:
 513+ return "unknown type";
 514+ }
 515+}
 516+
 517+bool
 518+conf_definer::validate(tree &t) const
 519+{
 520+bool ret = true;
 521+vector<block_definer *>::const_iterator bit, bend;
 522+vector<tree_entry>::iterator tit, tend;
 523+ for (tit = t.entries.begin(), tend = t.entries.end(); tit != tend; ++tit) {
 524+ for (bit = blocks.begin(), bend = blocks.end(); bit != bend; ++bit) {
 525+ if ((*bit)->name == tit->item_name) {
 526+ tit->item_touched = true;
 527+ ret = (*bit)->validate(*tit) && ret;
 528+ }
 529+ }
 530+ }
 531+ if (find_untouched(t))
 532+ return false;
 533+ return ret;
 534+}
 535+
 536+void
 537+conf_definer::set(tree &t) const
 538+{
 539+vector<block_definer *>::const_iterator bit, bend;
 540+vector<tree_entry>::iterator tit, tend;
 541+ for (tit = t.entries.begin(), tend = t.entries.end(); tit != tend; ++tit) {
 542+ for (bit = blocks.begin(), bend = blocks.end(); bit != bend; ++bit) {
 543+ if ((*bit)->name == tit->item_name) {
 544+ (*bit)->set(*tit);
 545+ }
 546+ }
 547+ }
 548+}
 549+
 550+} // namespace conf
Index: trunk/willow/src/willow/lexer.l
@@ -1,66 +1,141 @@
2 -%{
3 -/* @(#) $Header$ */
4 -/* This source code is in the public domain. */
52 /*
6 - * Willow: Lightweight HTTP reverse-proxy.
7 - * lexer: configuration file lexer.
 3+ * willow config lexer
 4+ * This source code is in the public domain.
 5+ * River Tarnell <river@attenuate.org>
86 */
9 -
10 -#if defined __SUNPRO_C || defined __DECC || defined __HP_cc
11 -# pragma ident "@(#)$Header$"
12 -#endif
 7+/* $Id: idl.l 2619 2006-10-13 15:46:38Z river $ */
138
14 -#include <string.h>
15 -#include <errno.h>
 9+%option noyywrap
 10+%option nounput
1611
17 -#include "willow.h"
 12+%{
 13+#include <iostream>
 14+#include <string>
 15+#include <cerrno>
 16+#include <cstdio>
 17+
 18+using std::strerror;
 19+using std::string;
 20+using std::fopen;
 21+
1822 #include "confparse.h"
1923 #include "y.tab.h"
2024
21 -int lineno = 1;
 25+struct func_attrs;
2226
23 -int
24 -yywrap(void)
25 -{
26 - return 1;
27 -}
 27+int yylex(void);
 28+
 29+#define MAXINCLUDE 10
 30+struct statestack_stru {
 31+ YY_BUFFER_STATE state;
 32+ string file;
 33+ int lineno;
 34+} statestack[MAXINCLUDE + 1];
 35+int inclevel;
 36+static int oldpos;
 37+
 38+#undef YY_INPUT
 39+
 40+#define YY_FATAL_ERROR(msg) conf::catastrophic_error(msg)
 41+
2842 %}
2943
30 -%x COMMENT
 44+ws [ ]*
 45+digit [0-9]
 46+number {digit}{digit}*
 47+qstring \"[^\"\n]*[\"\n]
 48+string [a-zA-Z_\~][a-zA-Z0-9_-]*
 49+include %{ws}include{ws}(\<.*\>|\".*\")
 50+pragma %{ws}pragma[ \t]{ws}.*$
 51+if %{ws}if[ \t]{ws}[a-zA-Z_][a-zA-Z0-9_]*$
 52+else %{ws}else{ws}$
 53+endif %{ws}endif{ws}$
3154
32 -ws [ \t]*
33 -number [0-9][0-9]*
34 -comment #.*
35 -qstring \"[^\"\n]*[\"]
36 -string [a-zA-Z_\~][a-zA-Z0-9_-]*
3755
 56+%x COMMENT IGNORING
3857 %%
3958
40 -^[ \t]*"/*" { BEGIN COMMENT; }
41 -^[ \t]*"/*".*"*/"[ \t]*\n { lineno++; }
 59+^.* {
 60+ conf::linebuf.assign(yytext, yyleng);
 61+ oldpos = conf::curpos = 0;
 62+ yyless(0);
 63+ }
 64+<IGNORING>{if} { conf::report_parse_error("%%if may not be nested"); }
 65+<IGNORING>{endif} { BEGIN 0; }
 66+<IGNORING>{else} { BEGIN 0; }
 67+<IGNORING>. ;
 68+<IGNORING>\n { conf::lineno++; }
4269
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;
 70+"/*" { BEGIN COMMENT; }
 71+<COMMENT>\*\/ { BEGIN 0; }
 72+<COMMENT>. ;
 73+<COMMENT>\n { conf::lineno++; conf::curpos = 0; }
 74+\/\/.*$ ;
 75+\n conf::lineno++;
 76+{include} {
 77+ FILE *f;
 78+ char *s, *t, c;
 79+ string file, path;
 80+ s = yytext + yyleng - 1;
 81+ c = *s;
 82+ t = s - 1;
 83+ while (*t != c)
 84+ t--;
 85+ file.assign(t + 1, s);
 86+ if (conf::find_include(file) == false) {
 87+ conf::catastrophic_error("cannot locate include file");
 88+ } else if ((f = fopen(file.c_str(), "r")) == NULL) {
 89+ string error = "cannot open include: ";
 90+ error += file; error += " ";
 91+ error += strerror(errno);
 92+ conf::catastrophic_error(error.c_str());
 93+ } else {
 94+ if (inclevel + 1 == MAXINCLUDE) {
 95+ conf::catastrophic_error("maximum include depth reached");
 96+ } else {
 97+ statestack[inclevel].state = YY_CURRENT_BUFFER;
 98+ statestack[inclevel].lineno = conf::lineno;
 99+ statestack[inclevel].file = conf::current_file;
 100+ inclevel++;
 101+ statestack[inclevel].state = yy_create_buffer(f, YY_BUF_SIZE);
 102+ conf::current_file = file;
 103+ conf::lineno = 1;
 104+ yy_switch_to_buffer(statestack[inclevel].state);
 105+ }
56106 }
57 - return NUMBER;
58107 }
59 -{string} { yylval.string = wstrdup(yytext);
60 - return STRING;
 108+<<EOF>> {
 109+ if (inclevel) {
 110+ yy_delete_buffer(statestack[inclevel].state);
 111+ yy_switch_to_buffer(statestack[inclevel - 1].state);
 112+ inclevel--;
 113+ conf::current_file = statestack[inclevel].file;
 114+ conf::lineno = statestack[inclevel].lineno;
 115+ } else
 116+ yyterminate();
61117 }
62 -{qstring} { yylval.string = wstrdup(yytext + 1);
63 - yylval.string[yyleng - 2] = '\0';
 118+
 119+\t { oldpos = conf::curpos; conf::curpos += 8; }
 120+{ws} { oldpos = conf::curpos; conf::curpos += yyleng; }
 121+var { oldpos = conf::curpos; conf::curpos += yyleng; return VAR; }
 122+template { oldpos = conf::curpos; conf::curpos += yyleng; return TEMPLATE; }
 123+from { oldpos = conf::curpos; conf::curpos += yyleng; return FROM; }
 124+\.\. { oldpos = conf::curpos; conf::curpos += yyleng; return TWODOTS; }
 125+
 126+{qstring} {
 127+ yylval.string_ = new std::string(yytext + 1, yyleng - 2);
 128+ oldpos = conf::curpos; conf::curpos += yyleng;
64129 return QSTRING;
65130 }
66 -. { return yytext[0];
 131+{string} {
 132+ yylval.string_ = new std::string(yytext, yyleng);
 133+ oldpos = conf::curpos; conf::curpos += yyleng;
 134+ return STRING;
67135 }
 136+{number} {
 137+ yylval.number = atoi(yytext);
 138+ oldpos = conf::curpos; conf::curpos += yyleng;
 139+ return NUMBER;
 140+ }
 141+. { oldpos = conf::curpos; conf::curpos++; return yytext[0]; }
 142+%%
Index: trunk/willow/src/willow/wcache.cc
@@ -0,0 +1,530 @@
 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+LIST_HEAD(key_idx_head, key_idx_entry);
 71+struct key_idx_bucket {
 72+ key_idx_head head;
 73+} key_idx[HASH_ELEMS];
 74+
 75+TAILQ_HEAD(objlist, cache_object) objects;
 76+
 77+static int
 78+cache_open(cache_object *obj, int flags, int mode)
 79+{
 80+ char *path;
 81+ int plen;
 82+ int i;
 83+
 84+ plen = strlen(config.caches[0].dir) + 1 + sizeof(CACHEDIR) + 1 + 6 + int_max_len;
 85+ path = (char *)wmalloc(plen + 1);
 86+ sprintf(path, "%s/%s/%s", config.caches[0].dir, CACHEDIR, obj->co_path);
 87+ if (mode)
 88+ unlink(path);
 89+ i = open(path, flags, mode);
 90+ wfree(path);
 91+ return i;
 92+}
 93+
 94+static void
 95+cache_unlink(cache_object *obj)
 96+{
 97+ char *path;
 98+ int plen;
 99+ int i;
 100+
 101+ plen = strlen(config.caches[0].dir) + 1 + sizeof(CACHEDIR) + 1 + 6 + int_max_len;
 102+ path = (char *)wmalloc(plen + 1);
 103+ sprintf(path, "%s/%s/%s", config.caches[0].dir, CACHEDIR, obj->co_path);
 104+ unlink(path);
 105+ wfree(path);
 106+ return;
 107+}
 108+
 109+void
 110+wcache_setupfs(void)
 111+{
 112+ int i, j, k;
 113+struct cachedir *cd;
 114+struct cache_state state;
 115+
 116+ for (cd = config.caches; cd < config.caches + config.ncaches; ++cd) {
 117+ size_t len, dlen;
 118+ char *dir;
 119+
 120+ dlen = strlen(cd->dir) + sizeof(CACHEDIR) + 2 + 6 /* 0/1/2/ */;
 121+ if ((dir = (char *)wmalloc(dlen)) == NULL)
 122+ outofmemory();
 123+
 124+ snprintf(dir, dlen, "%s/%s", cd->dir, CACHEDIR);
 125+
 126+ /* create base directory if it doesn't exist */
 127+ /*LINTED unsafe mkdir*/
 128+ if (mkdir(cd->dir, 0700) < 0 || mkdir(dir, 0700) < 0) {
 129+ wlog(WLOG_ERROR, "%s: mkdir: %s", cd->dir, strerror(errno));
 130+ exit(8);
 131+ }
 132+
 133+ for (i = 0; i < 10; ++i) {
 134+ snprintf(dir, dlen, "%s/%s/%d", cd->dir, CACHEDIR, i);
 135+
 136+ /*LINTED unsafe mkdir*/
 137+ if (mkdir(dir, 0700) < 0) {
 138+ wlog(WLOG_ERROR, "%s: mkdir: %s", dir, strerror(errno));
 139+ exit(8);
 140+ }
 141+
 142+ for (j = 0; j < 10; ++j) {
 143+ snprintf(dir, dlen, "%s/%s/%d/%d", cd->dir, CACHEDIR, i, j);
 144+ /*LINTED unsafe mkdir*/
 145+ if (mkdir(dir, 0700) < 0) {
 146+ wlog(WLOG_ERROR, "%s: mkdir: %s", dir, strerror(errno));
 147+ exit(8);
 148+ }
 149+ for (k = 0; k < 10; ++k) {
 150+ snprintf(dir, dlen, "%s/%s/%d/%d/%d", cd->dir, CACHEDIR, i, j, k);
 151+ /*LINTED unsafe mkdir*/
 152+ if (mkdir(dir, 0700) < 0) {
 153+ wlog(WLOG_ERROR, "%s: mkdir: %s", dir, strerror(errno));
 154+ exit(8);
 155+ }
 156+ }
 157+ }
 158+ }
 159+ wfree(dir);
 160+ wlog(WLOG_NOTICE, "created cache directory structure for %s", cd->dir);
 161+ }
 162+ wcache_init(0);
 163+
 164+ bzero(&state, sizeof(state));
 165+ state.cs_id = 1000;
 166+ state.cs_size = 0;
 167+ cache_writestate(&state);
 168+
 169+ wlog(WLOG_NOTICE, "wrote initial cache state");
 170+ wcache_shutdown();
 171+}
 172+
 173+void
 174+wcache_shutdown(void)
 175+{
 176+ cache_writestate(&state);
 177+}
 178+
 179+void
 180+wcache_init(int readstate)
 181+{
 182+struct cachedir *cd;
 183+ int i;
 184+
 185+ if (config.ncaches == 0) {
 186+ wlog(WLOG_WARNING, "no cache directories specified");
 187+ return;
 188+ }
 189+
 190+ /* only one cache dir supported for now... */
 191+ for (cd = config.caches; cd < config.caches + config.ncaches; ++cd) {
 192+ }
 193+
 194+ int_max_len = (int) log10((double) INT_MAX) + 1;
 195+
 196+ TAILQ_INIT(&objects);
 197+
 198+ cache_getstate(&state);
 199+ if (readstate) {
 200+ expire_sched();
 201+ }
 202+}
 203+
 204+static void
 205+expire_sched(void)
 206+{
 207+ expire_tv.tv_usec = 0;
 208+ expire_tv.tv_sec = config.cache_expevery;
 209+ evtimer_set(&expire_ev, run_expiry, NULL);
 210+ event_add(&expire_ev, &expire_tv);
 211+}
 212+
 213+void
 214+wcache_release(cache_object *obj, int comp)
 215+{
 216+ WDEBUG((WLOG_DEBUG, "release %s, comp=%d", obj->co_key, comp));
 217+
 218+ if (comp) {
 219+ if (!obj->co_complete)
 220+ state.cs_size += obj->co_size;
 221+ obj->co_complete = 1;
 222+ } else if (!obj->co_complete) {
 223+ wcache_evict(obj);
 224+ }
 225+}
 226+
 227+static void
 228+wcache_evict(cache_object *obj)
 229+{
 230+ TAILQ_REMOVE(&objects, obj, entries);
 231+ cache_unlink(obj);
 232+ state.cs_size -= obj->co_size;
 233+ idx_rem(obj);
 234+ wcache_free_object(obj);
 235+}
 236+
 237+struct cache_object *
 238+wcache_find_object(const char *key, int *fd, int flags)
 239+{
 240+struct cache_object *co;
 241+
 242+ WDEBUG((WLOG_DEBUG, "wcache_find_object: looking for %s", key));
 243+ co = idx_find(key);
 244+
 245+ if (co) {
 246+ /*
 247+ * If they only want it for writing, fail if it exists
 248+ */
 249+ if (flags & WCACHE_WRONLY)
 250+ return NULL;
 251+
 252+ if (!strcmp(key, co->co_key)) {
 253+ if (!co->co_complete) {
 254+ return NULL;
 255+ }
 256+ *fd = cache_open(co, O_RDONLY, 0);
 257+ WDEBUG((WLOG_DEBUG, "found! fd=%d", *fd));
 258+ if (*fd == -1) {
 259+ wlog(WLOG_WARNING, "opening cache file %s: %s", co->co_path, strerror(errno));
 260+ co = NULL;
 261+ }
 262+ return co;
 263+ }
 264+ }
 265+
 266+ co = wcache_new_object(key);
 267+ idx_add(co);
 268+ if ((*fd = cache_open(co, O_WRONLY | O_CREAT | O_EXCL, 0600)) == -1) {
 269+ wlog(WLOG_WARNING, "opening cached file: %s", strerror(errno));
 270+ wcache_free_object(co);
 271+ return NULL;
 272+ }
 273+
 274+ return co;
 275+}
 276+
 277+static void
 278+cache_writestate(cache_state *state)
 279+{
 280+ FILE *stfil;
 281+ char *stpath;
 282+ int stlen;
 283+struct cache_object *obj;
 284+
 285+ stlen = strlen(config.caches[0].dir) + 1 + 5 + 1;
 286+ if ((stpath = (char *)wmalloc(stlen)) == NULL)
 287+ outofmemory();
 288+ snprintf(stpath, stlen, "%s/%s", config.caches[0].dir, "state");
 289+ if ((stfil = fopen(stpath, "w")) == NULL) {
 290+ wlog(WLOG_WARNING, "opening cache dir %s: %s", stpath, strerror(errno));
 291+ exit(8);
 292+ }
 293+ fprintf(stfil, "%lld %lld\n", state->cs_id, state->cs_size);
 294+ TAILQ_FOREACH(obj, &objects, entries) {
 295+ if (!obj->co_complete)
 296+ continue;
 297+ fprintf(stfil, "%s %s %d %lu %lu %d %lu\n", obj->co_key, obj->co_path, obj->co_size,
 298+ (unsigned long) obj->co_time, (unsigned long) obj->co_lru, obj->co_id,
 299+ (unsigned long) obj->co_expires);
 300+ }
 301+ fclose(stfil);
 302+}
 303+
 304+static void
 305+cache_getstate(cache_state *state)
 306+{
 307+ FILE *stfil;
 308+ char *stpath;
 309+ int stlen;
 310+struct cache_object *obj;
 311+ int i;
 312+ char *s;
 313+ size_t l;
 314+
 315+ stlen = strlen(config.caches[0].dir) + 1 + 5 + 1;
 316+ if ((stpath = (char *)wmalloc(stlen)) == NULL)
 317+ outofmemory();
 318+ snprintf(stpath, stlen, "%s/%s", config.caches[0].dir, "state");
 319+ if ((stfil = fopen(stpath, "r")) == NULL) {
 320+ wlog(WLOG_WARNING, "opening cache state %s: %s", stpath, strerror(errno));
 321+ wlog(WLOG_WARNING, "using default cache state");
 322+ state->cs_id = 1000;
 323+ return;
 324+ }
 325+
 326+ if (fscanf(stfil, "%lld %lld\n", &state->cs_id, &state->cs_size) != 2) {
 327+ wlog(WLOG_ERROR, "data format error in cache state file %s", stpath);
 328+ exit(8);
 329+ }
 330+
 331+ s = (char *)wmalloc(65535);
 332+ while (fgets(s, 65534, stfil)) {
 333+ char url[65535], path[128];
 334+ int size, time, lru, id, expires;
 335+ struct cache_object *obj;
 336+ if (sscanf(s, "%65534s %127s %d %d %d %d %d", url, path, &size, &time, &lru, &id, &expires) != 7) {
 337+ wlog(WLOG_ERROR, "data format error in cache state file %s", stpath);
 338+ exit(8);
 339+ }
 340+ obj = (cache_object *)wmalloc(sizeof(*obj));
 341+ bzero(obj, sizeof(*obj));
 342+ obj->co_key = wstrdup(url);
 343+ obj->co_size = size;
 344+ obj->co_path = wstrdup(path);
 345+ obj->co_complete = 1;
 346+ obj->co_time = time;
 347+ obj->co_lru = lru;
 348+ obj->co_id = id;
 349+ obj->co_expires = expires;
 350+ TAILQ_INSERT_TAIL(&objects, obj, entries);
 351+ idx_add(obj);
 352+ WDEBUG((WLOG_DEBUG, "load %s %s from cache", obj->co_key, obj->co_path));
 353+ }
 354+
 355+}
 356+
 357+static int
 358+cache_next_id(void)
 359+{
 360+ int i;
 361+ i = state.cs_id;
 362+ ++state.cs_id;
 363+ return i;
 364+}
 365+
 366+static struct cache_object *
 367+wcache_new_object(const char *key)
 368+{
 369+struct cache_object *ret;
 370+ int i;
 371+ char *p, *s, a[11];
 372+
 373+ if ((ret = (cache_object *)wcalloc(1, sizeof(*ret))) == NULL) {
 374+ outofmemory();
 375+ /*NOTREACHED*/
 376+ }
 377+
 378+ ret->co_id = cache_next_id();
 379+ ret->co_key = wstrdup(key);
 380+
 381+ assert(ret->co_id > 999);
 382+ ret->co_plen = int_max_len + 6;
 383+ if ((ret->co_path = (char *)wmalloc(ret->co_plen + 1)) == NULL) {
 384+ outofmemory();
 385+ /*NOTREACHED*/
 386+ }
 387+ p = ret->co_path;
 388+ snprintf(a, 10, "%d", ret->co_id);
 389+ s = a + strlen(a) - 1;
 390+ WDEBUG((WLOG_DEBUG, "id=%d a=%s", ret->co_id, a));
 391+
 392+ for (i = 0; i < 3; ++i) {
 393+ *p++ = *s--;
 394+ *p++ = '/';
 395+ }
 396+ *p = '\0';
 397+ if (strlcat(ret->co_path, a, ret->co_plen + 1) >= ret->co_plen + 1)
 398+ abort();
 399+ WDEBUG((WLOG_DEBUG, "new object path is [%s], len %d", ret->co_path, ret->co_plen));
 400+
 401+ TAILQ_INSERT_HEAD(&objects, ret, entries);
 402+ return ret;
 403+}
 404+
 405+static void
 406+wcache_free_object(cache_object *obj)
 407+{
 408+}
 409+
 410+static void
 411+run_expiry(int fd, short ev, void *data)
 412+{
 413+ w_size_t wantsize;
 414+ int i;
 415+struct cache_object *obj;
 416+
 417+ WDEBUG((WLOG_DEBUG, "expire: start, run every %d, cache is %lld bytes large",
 418+ config.cache_expevery, state.cs_size));
 419+
 420+ cache_writestate(&state);
 421+ wantsize = (w_size_t) (config.caches[0].maxsize * ((100.0-config.cache_expthresh)/100));
 422+ if (state.cs_size <= wantsize) {
 423+ WDEBUG((WLOG_DEBUG, "expire: cache only %lld bytes large", state.cs_size));
 424+ expire_sched();
 425+ return;
 426+ }
 427+ while (state.cs_size > wantsize) {
 428+ struct cache_object *obj = TAILQ_LAST(&objects, objlist);
 429+ WDEBUG((WLOG_DEBUG, "expiring some objects, size=%lld, want=%lld, obj=%p, last=%p, *last=%p",
 430+ state.cs_size, wantsize, obj, objects.tqh_last, *objects.tqh_last));
 431+ if (!obj)
 432+ break;
 433+ wcache_evict(obj);
 434+ }
 435+ expire_sched();
 436+}
 437+
 438+static void
 439+idx_add(cache_object *obj)
 440+{
 441+ struct key_idx_head *head = &key_idx[hash((ub1 *)obj->co_key)].head;
 442+ struct key_idx_entry *entry = (key_idx_entry *)wmalloc(sizeof(*entry));
 443+ bzero(entry, sizeof(*entry));
 444+ entry->obj = obj;
 445+ LIST_INSERT_HEAD(head, entry, entries);
 446+}
 447+
 448+static struct cache_object *
 449+idx_find(const char *key)
 450+{
 451+ struct key_idx_head *head = &key_idx[hash((ub1 *)key)].head;
 452+ struct key_idx_entry *entry;
 453+ LIST_FOREACH(entry, head, entries)
 454+ if (!strcmp(entry->obj->co_key, key))
 455+ return entry->obj;
 456+ return NULL;
 457+}
 458+
 459+static void
 460+idx_rem(cache_object *obj)
 461+{
 462+ struct key_idx_head *head = &key_idx[hash((ub1 *)obj->co_key)].head;
 463+ struct key_idx_entry *entry;
 464+ LIST_FOREACH(entry, head, entries)
 465+ if (entry->obj == obj)
 466+ LIST_REMOVE(entry, entries);
 467+}
 468+
 469+#define mix(a,b,c) \
 470+{ \
 471+ a -= b; a -= c; a ^= (c>>13); \
 472+ b -= c; b -= a; b ^= (a<<8); \
 473+ c -= a; c -= b; c ^= (b>>13); \
 474+ a -= b; a -= c; a ^= (c>>12); \
 475+ b -= c; b -= a; b ^= (a<<16); \
 476+ c -= a; c -= b; c ^= (b>>5); \
 477+ a -= b; a -= c; a ^= (c>>3); \
 478+ b -= c; b -= a; b ^= (a<<10); \
 479+ c -= a; c -= b; c ^= (b>>15); \
 480+}
 481+
 482+/*
 483+ * By Bob Jenkins, 1996. bob_jenkins@burtleburtle.net. You may use this
 484+ * code any way you wish, private, educational, or commercial. It's free.
 485+ *
 486+ * See http://burtleburtle.net/bob/hash/evahash.html
 487+ */
 488+
 489+ub4 hash(const ub1 *k)
 490+{
 491+ register ub4 a,b,c,len;
 492+ register ub4 length = strlen((char *)k);
 493+ register ub4 initval = 0;
 494+
 495+ /* Set up the internal state */
 496+ len = length;
 497+ a = b = 0x9e3779b9; /* the golden ratio; an arbitrary value */
 498+ c = initval; /* the previous hash value */
 499+
 500+ /*---------------------------------------- handle most of the key */
 501+ while (len >= 12)
 502+ {
 503+ a += (k[0] +((ub4)k[1]<<8) +((ub4)k[2]<<16) +((ub4)k[3]<<24));
 504+ b += (k[4] +((ub4)k[5]<<8) +((ub4)k[6]<<16) +((ub4)k[7]<<24));
 505+ c += (k[8] +((ub4)k[9]<<8) +((ub4)k[10]<<16)+((ub4)k[11]<<24));
 506+ mix(a,b,c);
 507+ k += 12; len -= 12;
 508+ }
 509+
 510+ /*------------------------------------- handle the last 11 bytes */
 511+ c += length;
 512+ switch(len) /* all the case statements fall through */
 513+ {
 514+ case 11: c+=((ub4)k[10]<<24);
 515+ case 10: c+=((ub4)k[9]<<16);
 516+ case 9 : c+=((ub4)k[8]<<8);
 517+ /* the first byte of c is reserved for the length */
 518+ case 8 : b+=((ub4)k[7]<<24);
 519+ case 7 : b+=((ub4)k[6]<<16);
 520+ case 6 : b+=((ub4)k[5]<<8);
 521+ case 5 : b+=k[4];
 522+ case 4 : a+=((ub4)k[3]<<24);
 523+ case 3 : a+=((ub4)k[2]<<16);
 524+ case 2 : a+=((ub4)k[1]<<8);
 525+ case 1 : a+=k[0];
 526+ /* case 0: nothing left to add */
 527+ }
 528+ mix(a,b,c);
 529+ /*-------------------------------------------- report the result */
 530+ return c & hashmask(HASH_BITS);
 531+}
Index: trunk/willow/src/willow/wconfig.cc
@@ -0,0 +1,164 @@
 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+using namespace conf;
 34+
 35+#define CONFIGFILE SYSCONFDIR "/willow.conf"
 36+
 37+vector<listener *> listeners;
 38+struct configuration config;
 39+
 40+static void
 41+set_backend(conf::tree_entry &e)
 42+{
 43+value const *val;
 44+int port = 80;
 45+ if ((val = e/"port") != NULL)
 46+ port = CONF_AINTVAL(*val);
 47+ add_backend(e.item_key, port);
 48+}
 49+
 50+static void
 51+set_listen(conf::tree_entry &e)
 52+{
 53+value const *val;
 54+int port = 80;
 55+struct listener *nl = new listener;
 56+ if ((val = e/"port") != NULL)
 57+ port = CONF_AINTVAL(*val);
 58+ listeners.push_back(nl);
 59+
 60+ nl->port = port;
 61+ nl->name = e.item_key;
 62+ nl->addr.sin_family = AF_INET;
 63+ nl->addr.sin_port = htons(nl->port);
 64+ nl->addr.sin_addr.s_addr = inet_addr(nl->name.c_str());
 65+ wlog(WLOG_NOTICE, "listening on %s:%d", e.item_key.c_str(), port);
 66+}
 67+
 68+static bool
 69+validate_log_facility(tree_entry &e, value &v)
 70+{
 71+ return true;
 72+}
 73+
 74+static void
 75+set_log_facility(tree_entry &e, value &v)
 76+{
 77+}
 78+
 79+static void
 80+set_cache(tree_entry &e)
 81+{
 82+value *v;
 83+ v = e/"size";
 84+ config.caches = (cachedir *)wrealloc(config.caches, sizeof(*config.caches) * (config.ncaches + 1));
 85+ config.caches[config.ncaches].dir = wstrdup(e.item_key.c_str());
 86+ config.caches[config.ncaches].maxsize = v->cv_values[0].av_intval;
 87+ wlog(WLOG_NOTICE, "cache dir \"%s\", size %d bytes",
 88+ config.caches[config.ncaches].dir,
 89+ config.caches[config.ncaches].maxsize);
 90+ config.ncaches++;
 91+}
 92+
 93+extern int parse_error;
 94+
 95+bool
 96+read_config(string const &file)
 97+{
 98+conf_definer conf;
 99+tree *t;
 100+conf
 101+ .block("log")
 102+ .value("level", simple_range(0, 3), set_int(logging.level))
 103+ .value("file", nonempty_qstring(), set_qstring(logging.file))
 104+ .value("syslog", simple_yesno(), set_yesno(logging.syslog))
 105+ .value("facility", func(validate_log_facility), func(set_log_facility))
 106+ .value("access-log", nonempty_qstring(), set_qstring(config.access_log))
 107+
 108+ .block("cache")
 109+ .value("expire-every", simple_time(), set_time(config.cache_expevery))
 110+ .value("expire-threshold", simple_range(0, 100), set_int(config.cache_expthresh))
 111+ .value("compress", simple_yesno(), set_yesno(config.compress))
 112+ .value("compress-level", simple_range(1, 9), set_int(config.complevel))
 113+ .value("backend-retry", simple_time(), set_time(config.backend_retry))
 114+ .value("cache-private", simple_yesno(), set_yesno(config.cache_private))
 115+ .value("use-carp", simple_yesno(), set_yesno(config.use_carp))
 116+
 117+ .block("cache-dir", require_name)
 118+ .end(func(set_cache))
 119+ .value("size", simple_time(), ignore())
 120+
 121+ .block("listen", require_name)
 122+ .end(func(set_listen))
 123+ .value("port", simple_range(1, 65535), ignore())
 124+
 125+ .block("backend", require_name)
 126+ .end(func(set_backend))
 127+ .value("port", simple_range(1, 65535), ignore())
 128+ ;
 129+
 130+ if ((t = conf::parse_file(file)) == NULL)
 131+ return false;
 132+ if (!conf.validate(*t))
 133+ return false;
 134+ conf.set(*t);
 135+ global_conf_tree = *t;
 136+ return true;
 137+}
 138+
 139+void
 140+wconfig_init(const char *file)
 141+{
 142+int nerrors = 0;
 143+ if (file == NULL)
 144+ file = CONFIGFILE;
 145+ conf::current_file = file;
 146+
 147+ wlog(WLOG_NOTICE, "loading configuration from %s", conf::current_file.c_str());
 148+ if (!read_config(file)) {
 149+ wlog(WLOG_ERROR, "cannot load configuration");
 150+ nerrors++;
 151+ }
 152+
 153+ if (!listeners.size()) {
 154+ wlog(WLOG_ERROR, "no listeners defined");
 155+ nerrors++;
 156+ }
 157+ if (!backends.size()) {
 158+ wlog(WLOG_ERROR, "no backends defined");
 159+ nerrors++;
 160+ }
 161+ if (nerrors) {
 162+ wlog(WLOG_ERROR, "%d error(s) in configuration file. cannot continue.", nerrors);
 163+ exit(8);
 164+ }
 165+}
Index: trunk/willow/src/willow/Makefile.in
@@ -2,7 +2,18 @@
33 BINDIR = @bindir@
44 CPPFLAGS = -I../include
55
6 -BASESRCS = willow.c wconfig.c whttp.c wbackend.c whttp_entity.c wcache.c confparse.c
 6+BASESRCS = \
 7+ willow.cc \
 8+ wconfig.cc \
 9+ whttp.cc \
 10+ wbackend.cc \
 11+ whttp_entity.cc \
 12+ wcache.cc \
 13+ confparse.cc \
 14+ wnet.cc \
 15+ wnet_libevent.cc \
 16+ wlog.cc
 17+OBJS= willow.o wconfig.o whttp.o wbackend.o whttp_entity.o wcache.o confparse.o wnet.o wlog.o wnet_libevent.o
718 SRCS=$(BASESRCS)
819 OBJADD = @LIBOBJS@ y.tab.o lex.yy.o
920
@@ -11,20 +22,22 @@
1223 daemon.c \
1324 Makefile.in
1425
15 -LDFLAGS = -L$(SRCROOT)/src/lib/wnet -lwillownet \
16 - -L$(SRCROOT)/src/lib/wlog -lwillowlog \
17 - $(LIBOBJS)
 26+LDFLAGS = $(LIBOBJS)
1827
 28+.SUFFIXES: .y .l .c .cc .o
 29+
1930 default: all
2031
21 -y.tab.o: y.tab.c
22 -lex.yy.o: lex.yy.c y.tab.h
 32+y.tab.o: y.tab.cc ../include/confparse.h ../include/willow.h
 33+lex.yy.o: lex.yy.cc y.tab.h ../include/confparse.h ../include/willow.h
2334
24 -y.tab.c y.tab.h: parser.y
 35+y.tab.cc y.tab.h: parser.y
2536 @echo " $(_YACC) -d parser.y"
2637 @$(_YACC) -d parser.y
27 -lex.yy.c: lexer.l y.tab.h
 38+ @mv y.tab.c y.tab.cc
 39+lex.yy.cc: lexer.l y.tab.h
2840 @echo " $(_LEX) lexer.l"
2941 @$(_LEX) lexer.l || rm -f lex.yy.c
 42+ @test -f lex.yy.c && mv lex.yy.c lex.yy.cc
3043
3144 @include@ @q@@top_srcdir@/mk/prog.mk@q@
Index: trunk/willow/src/willow/whttp_entity.cc
@@ -0,0 +1,1213 @@
 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(http_entity *entity)
 120+{
 121+ WDEBUG((WLOG_DEBUG, "free entity @ %p", entity));
 122+
 123+ header_free(&entity->he_headers);
 124+ if (entity->_he_frombuf) {
 125+ bufferevent_disable(entity->_he_frombuf, EV_READ | EV_WRITE);
 126+ bufferevent_free(entity->_he_frombuf);
 127+ }
 128+ if (entity->_he_tobuf) {
 129+ bufferevent_disable(entity->_he_tobuf, EV_READ | EV_WRITE);
 130+ bufferevent_free(entity->_he_tobuf);
 131+ }
 132+ if (entity->he_reqstr)
 133+ wfree(entity->he_reqstr);
 134+ if (!entity->he_flags.response) {
 135+ if (entity->he_rdata.request.host)
 136+ wfree(entity->he_rdata.request.host);
 137+ if (entity->he_rdata.request.path)
 138+ wfree(entity->he_rdata.request.path);
 139+ }
 140+ bzero(entity, sizeof(*entity));
 141+}
 142+
 143+void
 144+entity_set_response(http_entity *ent, int isresp)
 145+{
 146+ if (isresp) {
 147+ if (ent->he_flags.response)
 148+ return;
 149+ if (ent->he_rdata.request.path)
 150+ wfree(ent->he_rdata.request.path);
 151+ if (ent->he_rdata.request.host)
 152+ wfree(ent->he_rdata.request.host);
 153+ bzero(&ent->he_rdata.response, sizeof(ent->he_rdata.response));
 154+ ent->he_flags.response = 1;
 155+ } else {
 156+ if (!ent->he_flags.response)
 157+ return;
 158+ bzero(&ent->he_rdata.request, sizeof(ent->he_rdata.request));
 159+ ent->he_flags.response = 0;
 160+ }
 161+}
 162+
 163+void
 164+entity_read_headers(http_entity *entity, header_cb func, void *udata)
 165+{
 166+ entity->_he_cbdata = udata;
 167+ entity->_he_func = func;
 168+ entity->he_flags.hdr_only = 1;
 169+
 170+ WDEBUG((WLOG_DEBUG, "entity_read_headers: starting, source %d",
 171+ entity->he_source.fde.fde->fde_fd));
 172+ /* XXX source for an entity header read is _always_ an fde */
 173+ entity->_he_frombuf = bufferevent_new(entity->he_source.fde.fde->fde_fd,
 174+ entity_read_callback, NULL, entity_error_callback, entity);
 175+ bufferevent_disable(entity->_he_frombuf, EV_WRITE);
 176+ bufferevent_enable(entity->_he_frombuf, EV_READ);
 177+// wnet_register(entity->he_source.fde.fde->fde_fd, FDE_READ, entity_read_callback, entity);
 178+ //entity_read_callback(entity->he_source.fde);
 179+}
 180+
 181+void
 182+entity_send(fde *fde, http_entity *entity, header_cb cb, void *data, int flags)
 183+{
 184+ char status[4];
 185+ int wn_flags = 0;
 186+ char *hdr;
 187+struct header_list *hl;
 188+ int window = 15;
 189+
 190+ errno = 0;
 191+
 192+ entity->_he_func = cb;
 193+ entity->_he_cbdata = data;
 194+ entity->_he_target = fde;
 195+ entity->he_flags.hdr_only = 0;
 196+ if (!entity->he_flags.response && entity->he_rdata.request.reqtype == REQTYPE_POST) {
 197+ entity->he_source.fde._wrt = entity->he_rdata.request.contlen;
 198+ }
 199+
 200+ entity->_he_tobuf = bufferevent_new(entity->_he_target->fde_fd,
 201+ NULL, entity_send_target_write,
 202+ entity_send_target_error, entity);
 203+ bufferevent_disable(entity->_he_tobuf, EV_READ);
 204+ bufferevent_enable(entity->_he_tobuf, EV_WRITE);
 205+ if (entity->_he_frombuf) {
 206+ bufferevent_disable(entity->_he_frombuf, EV_READ);
 207+ bufferevent_disable(entity->_he_frombuf, EV_WRITE);
 208+ }
 209+ entity->_he_state = ENTITY_STATE_SEND_HDR;
 210+
 211+ WDEBUG((WLOG_DEBUG, "entity_send: writing to %d [%s], enc=%d", fde->fde_fd, fde->fde_desc,
 212+ entity->he_encoding));
 213+
 214+ if (entity->he_flags.response) {
 215+ evbuffer_add_printf(entity->_he_tobuf->output, "HTTP/1.1 %d %s\r\n",
 216+ entity->he_rdata.response.status, entity->he_rdata.response.status_str);
 217+ } else {
 218+ evbuffer_add_printf(entity->_he_tobuf->output, "%s %s HTTP/1.1\r\n",
 219+ request_string[entity->he_rdata.request.reqtype],
 220+ entity->he_rdata.request.path);
 221+ }
 222+
 223+ if (flags & ENT_CHUNKED_OKAY) {
 224+ struct header_list *contlen;
 225+ entity->he_flags.chunked = 1;
 226+ if (!entity->he_h_transfer_encoding)
 227+ header_add(&entity->he_headers, wstrdup("Transfer-Encoding"), wstrdup("chunked"));
 228+ if ((contlen = header_find(&entity->he_headers, "Content-Length")) != NULL)
 229+ header_remove(&entity->he_headers, contlen);
 230+ }
 231+
 232+ switch (entity->he_encoding) {
 233+ case E_NONE:
 234+ break;
 235+ case E_GZIP: case E_X_GZIP:
 236+ window += 16;
 237+ case E_DEFLATE: case E_X_DEFLATE: {
 238+ int err;
 239+
 240+ if ((err = deflateInit2(&entity->_he_zbuf, config.complevel, Z_DEFLATED,
 241+ window, 8, Z_DEFAULT_STRATEGY)) != Z_OK) {
 242+ wlog(WLOG_WARNING, "deflateInit: %s", zError(err));
 243+ entity->he_encoding = E_NONE;
 244+ }
 245+ break;
 246+ }
 247+ }
 248+ header_add(&entity->he_headers, wstrdup("Content-Encoding"),
 249+ wstrdup(ent_encodings[entity->he_encoding]));
 250+ for (hl = entity->he_headers.hl_next; hl; hl = hl->hl_next)
 251+ evbuffer_add_printf(entity->_he_tobuf->output, "%s: %s\r\n", hl->hl_name, hl->hl_value);
 252+ bufferevent_write(entity->_he_tobuf, const_cast<char *>("\r\n"), 2);
 253+}
 254+
 255+static void
 256+entity_error_callback(struct bufferevent *be, short what, void *d)
 257+{
 258+struct http_entity *entity = (http_entity *)d;
 259+
 260+ /*
 261+ * Some kind of error occured while we were reading from the backend.
 262+ */
 263+ WDEBUG((WLOG_DEBUG, "entity_error_callback called, what=%hd errno=%d %s", what,
 264+ errno, strerror(errno)));
 265+
 266+ if (what & EVBUFFER_EOF) {
 267+ /*
 268+ * End of file from backend.
 269+ */
 270+ if (entity->he_encoding) {
 271+ if (write_zlib_eof(entity) == -1) {
 272+ entity->_he_func(entity, entity->_he_cbdata, -1);
 273+ return;
 274+ }
 275+ }
 276+
 277+ if (entity->he_flags.chunked && !entity->he_flags.eof) {
 278+ WDEBUG((WLOG_DEBUG, "writing chunked data, append EOF"));
 279+ entity->he_flags.eof = 1;
 280+ bufferevent_enable(entity->_he_tobuf, EV_WRITE);
 281+ bufferevent_write(entity->_he_tobuf, const_cast<void *>((void *)"0\r\n"), 3);
 282+ return;
 283+ }
 284+ WDEBUG((WLOG_DEBUG, "entity_error_callback: EOF"));
 285+ entity->_he_func(entity, entity->_he_cbdata, 1);
 286+ //entity->he_flags.eof = 1;
 287+ return;
 288+ }
 289+
 290+ entity->he_flags.error = 1;
 291+ entity->_he_func(entity, entity->_he_cbdata, -1);
 292+}
 293+
 294+static int
 295+write_zlib_eof(http_entity *entity)
 296+{
 297+ int zerr;
 298+ unsigned char zbuf[16384];
 299+ int n;
 300+
 301+ entity->_he_zbuf.avail_in = 0;
 302+ entity->_he_zbuf.next_out = zbuf;
 303+ entity->_he_zbuf.avail_out = sizeof zbuf;
 304+
 305+ zerr = deflate(&entity->_he_zbuf, Z_FINISH);
 306+
 307+ if (zerr != Z_STREAM_END) {
 308+ wlog(WLOG_WARNING, "deflate: %s", zError(zerr));
 309+ deflateEnd(&entity->_he_zbuf);
 310+ return -1;
 311+ }
 312+ n = sizeof zbuf - entity->_he_zbuf.avail_out;
 313+ WDEBUG((WLOG_DEBUG, "writing zlib, append finish, left=%d avail=%d",
 314+ n, entity->_he_zbuf.avail_out));
 315+ if (n) {
 316+ bufferevent_enable(entity->_he_tobuf, EV_WRITE);
 317+ if (entity->he_flags.chunked)
 318+ evbuffer_add_printf(entity->_he_tobuf->output, "%x\r\n", n);
 319+ bufferevent_write(entity->_he_tobuf, zbuf, n);
 320+ if (entity->he_flags.chunked)
 321+ evbuffer_add_printf(entity->_he_tobuf->output, "\r\n");
 322+ }
 323+ deflateEnd(&entity->_he_zbuf);
 324+ return 0;
 325+}
 326+
 327+static void
 328+entity_read_callback(bufferevent *be, void *d)
 329+{
 330+struct http_entity *entity = (http_entity *) d;
 331+ int i;
 332+#define RD_BUFSZ 16386
 333+ char buf[RD_BUFSZ];
 334+ int wrote = 0, contdone = 0;
 335+
 336+ /*
 337+ * Data was available from the backend. If state is ENTITY_STATE_SEND_BODY,
 338+ * we're moving the request from backend->client, so do that. Otherwise,
 339+ * we're still reading header information.
 340+ */
 341+ WDEBUG((WLOG_DEBUG, "entity_read_callback: called, source %d",
 342+ entity->he_source.fde.fde->fde_fd));
 343+
 344+ if (entity->_he_state < ENTITY_STATE_SEND_BODY) {
 345+ if ((i = parse_headers(entity)) < 0) {
 346+ WDEBUG((WLOG_DEBUG, "entity_read_callback: parse_headers returned -1"));
 347+ entity->he_flags.error = 1;
 348+ entity->_he_func(entity, entity->_he_cbdata, i);
 349+ return;
 350+ }
 351+
 352+ WDEBUG((WLOG_DEBUG, "parse_headers returned; client now %d", entity->_he_state));
 353+
 354+ if (entity->_he_state == ENTITY_STATE_DONE) {
 355+ if (entity->he_flags.hdr_only) {
 356+ WDEBUG((WLOG_DEBUG, "entity_read_callback: client is ENTITY_STATE_DONE"));
 357+ bufferevent_disable(entity->_he_frombuf, EV_READ);
 358+ entity->_he_func(entity, entity->_he_cbdata, 0);
 359+ return;
 360+ } else
 361+ entity->_he_state = ENTITY_STATE_SEND_BODY;
 362+ }
 363+ //bufferevent_disable(entity->_he_frombuf, EV_READ);
 364+ //if (entity->he_flags.hdr_only)
 365+ return;
 366+ }
 367+
 368+ if (entity->he_flags.eof) {
 369+ entity->_he_func(entity, entity->_he_cbdata, 0);
 370+ return;
 371+ }
 372+
 373+ bufferevent_enable(entity->_he_tobuf, EV_WRITE);
 374+
 375+ if (entity->he_flags.eof) {
 376+ /*
 377+ * This means the last chunk was written (see below).
 378+ */
 379+ bufferevent_disable(entity->_he_frombuf, EV_READ);
 380+ entity->_he_func(entity, entity->_he_cbdata, 0);
 381+ bufferevent_enable(entity->_he_tobuf, EV_WRITE);
 382+ return;
 383+ }
 384+
 385+ /*
 386+ * While data is available, read it and forward. If we're using chunked encoding,
 387+ * don't read past the end of the chunk.
 388+ */
 389+ for (;;) {
 390+ size_t read;
 391+ size_t want = RD_BUFSZ;
 392+
 393+ /*
 394+ * If we're reading chunked data, check if we're starting a new chunk.
 395+ */
 396+ if ((entity->he_te & TE_CHUNKED) && entity->_he_chunk_size == 0) {
 397+ char *chunks;
 398+ if ((chunks = evbuffer_readline(entity->_he_frombuf->input)) == NULL)
 399+ return;
 400+ entity->_he_chunk_size = strtol(chunks, NULL, 16);
 401+ free(chunks);
 402+ WDEBUG((WLOG_DEBUG, "new chunk, size=%d", entity->_he_chunk_size));
 403+ if (entity->_he_chunk_size == 0) {
 404+ /*
 405+ * Zero-sized chunk = end of request.
 406+ *
 407+ * If this client is receiving TE:chunked data, we have to write
 408+ * the terminating block and finish up the next time round. If
 409+ * not, mark it finished now.
 410+ */
 411+ int more = 0;
 412+
 413+ if (entity->he_encoding) {
 414+ bufferevent_enable(entity->_he_tobuf, EV_WRITE);
 415+ write_zlib_eof(entity);
 416+ more = 1;
 417+ }
 418+
 419+ if (entity->he_flags.chunked) {
 420+ bufferevent_disable(entity->_he_frombuf, EV_READ);
 421+ bufferevent_enable(entity->_he_tobuf, EV_WRITE);
 422+ bufferevent_write(entity->_he_tobuf, const_cast<void *>((void *)"0\r\n"), 3);
 423+ more = 1;
 424+ }
 425+
 426+ if (more) {
 427+ entity->he_flags.eof = 1;
 428+ return;
 429+ }
 430+
 431+ bufferevent_disable(entity->_he_frombuf, EV_READ);
 432+ if (!wrote)
 433+ entity->_he_func(entity, entity->_he_cbdata, 0);
 434+ else
 435+ bufferevent_enable(entity->_he_tobuf, EV_WRITE);
 436+ return;
 437+ }
 438+ /* +2 for CRLF */
 439+ entity->_he_chunk_size += 2;
 440+ }
 441+
 442+ want = RD_BUFSZ;
 443+ if (entity->_he_chunk_size)
 444+ want = entity->_he_chunk_size;
 445+ else if (entity->he_source.fde._wrt)
 446+ want = entity->_he_chunk_size;
 447+
 448+ want = entity->_he_chunk_size ? entity->_he_chunk_size : RD_BUFSZ;
 449+
 450+ read = bufferevent_read(entity->_he_frombuf, buf, want);
 451+ WDEBUG((WLOG_DEBUG, "rw %d, got %d wrote=%d wrt=%d", want, read, wrote,
 452+ entity->he_source.fde._wrt));
 453+ if (read == 0) {
 454+ if (!wrote)
 455+ bufferevent_enable(entity->_he_frombuf, EV_READ);
 456+ else
 457+ bufferevent_disable(entity->_he_frombuf, EV_READ);
 458+ return;
 459+ }
 460+
 461+ if (entity->_he_chunk_size)
 462+ entity->_he_chunk_size -= read;
 463+ if (entity->he_source.fde._wrt) {
 464+ entity->he_source.fde._wrt -= read;
 465+ if (entity->he_source.fde._wrt == 0)
 466+ contdone = 1;
 467+ }
 468+
 469+ if ((entity->he_te & TE_CHUNKED) && entity->_he_chunk_size == 0)
 470+ /* subtract the +2 we added above */
 471+ read -= 2;
 472+
 473+ entity->he_size += read;
 474+
 475+ if (entity->he_cache_callback) {
 476+ entity->he_cache_callback(buf, read, entity->he_cache_callback_data);
 477+ }
 478+ bufferevent_enable(entity->_he_tobuf, EV_WRITE);
 479+
 480+ if (write_data(entity, buf, read) == -1) {
 481+ entity->_he_func(entity, entity->_he_cbdata, -1);
 482+ return;
 483+ }
 484+
 485+ wrote++;
 486+ if (contdone) {
 487+ bufferevent_disable(entity->_he_frombuf, EV_READ);
 488+ //entity->_he_func(entity, entity->_he_cbdata, 0);
 489+ entity->he_flags.eof = 1;
 490+ return;
 491+ }
 492+ }
 493+
 494+ bufferevent_disable(entity->_he_frombuf, EV_READ);
 495+}
 496+
 497+static int
 498+write_data(http_entity *entity, void *buf, size_t len)
 499+{
 500+static unsigned char zbuf[ZLIB_BLOCK * 2];
 501+
 502+ switch (entity->he_encoding) {
 503+ case E_NONE:
 504+ if (entity->he_flags.chunked)
 505+ evbuffer_add_printf(entity->_he_tobuf->output, "%x\r\n", len);
 506+ bufferevent_write(entity->_he_tobuf, buf, len);
 507+ if (entity->he_flags.chunked)
 508+ evbuffer_add_printf(entity->_he_tobuf->output, "\r\n");
 509+ return 0;
 510+
 511+ case E_DEFLATE: case E_X_DEFLATE: case E_GZIP: case E_X_GZIP: {
 512+ int zerr;
 513+
 514+ entity->_he_zbuf.next_in = (unsigned char *)buf;
 515+ entity->_he_zbuf.avail_in = len;
 516+ entity->_he_zbuf.next_out = zbuf;
 517+ entity->_he_zbuf.avail_out = sizeof zbuf;
 518+ while (entity->_he_zbuf.avail_in) {
 519+ zerr = deflate(&entity->_he_zbuf, Z_SYNC_FLUSH);
 520+ if (zerr != Z_OK) {
 521+ wlog(WLOG_WARNING, "deflate: %s", zError(zerr));
 522+ return -1;
 523+ }
 524+ WDEBUG((WLOG_DEBUG, "avail_in=%d avail_out=%d",
 525+ entity->_he_zbuf.avail_in, entity->_he_zbuf.avail_out));
 526+ if (entity->he_flags.chunked)
 527+ evbuffer_add_printf(entity->_he_tobuf->output, "%x\r\n",
 528+ (sizeof zbuf - entity->_he_zbuf.avail_out));
 529+ bufferevent_write(entity->_he_tobuf, zbuf,
 530+ (sizeof zbuf - entity->_he_zbuf.avail_out));
 531+ if (entity->he_flags.chunked)
 532+ evbuffer_add_printf(entity->_he_tobuf->output, "\r\n");
 533+ entity->_he_zbuf.next_out = zbuf;
 534+ entity->_he_zbuf.avail_out = sizeof zbuf;
 535+ }
 536+ return 0;
 537+ }
 538+ }
 539+ abort();
 540+}
 541+
 542+static void
 543+entity_send_target_read(struct bufferevent *buf, void *d)
 544+{
 545+ /*
 546+ * Read from target possible. This never happens.
 547+ */
 548+}
 549+
 550+static void
 551+entity_send_target_write(struct bufferevent *buf, void *d)
 552+{
 553+struct http_entity *entity = (http_entity *)d;
 554+static char fbuf[ZLIB_BLOCK];
 555+
 556+ WDEBUG((WLOG_DEBUG, "entity_send_target_write: eof=%d, state=%d",
 557+ entity->he_flags.eof, entity->_he_state));
 558+
 559+ if (entity->he_flags.eof) {
 560+ if (entity->_he_frombuf)
 561+ bufferevent_disable(entity->_he_frombuf, EV_READ);
 562+ bufferevent_disable(entity->_he_tobuf, EV_WRITE);
 563+ entity->_he_func(entity, entity->_he_cbdata, 0);
 564+ return;
 565+ }
 566+
 567+ /*
 568+ * Write to target completed.
 569+ */
 570+ if (entity->_he_state == ENTITY_STATE_SEND_HDR) {
 571+ /*
 572+ * Sending headers completed. Decide what to do next.
 573+ */
 574+ switch (entity->he_source_type) {
 575+ case ENT_SOURCE_NONE:
 576+ /* no body for this request */
 577+ WDEBUG((WLOG_DEBUG, "entity_send_target_write: no body, return immediately"));
 578+ entity->_he_func(entity, entity->_he_cbdata, 0);
 579+ return;
 580+
 581+ case ENT_SOURCE_BUFFER:
 582+ /* write buffer, callback when done */
 583+ WDEBUG((WLOG_DEBUG, "entity_send_target_write: source is buffer, %d bytes",
 584+ entity->he_source.buffer.len));
 585+ entity->_he_state = ENTITY_STATE_SEND_BUF;
 586+ bufferevent_write(entity->_he_tobuf,
 587+ (void *)entity->he_source.buffer.addr,
 588+ entity->he_source.buffer.len);
 589+ return;
 590+
 591+ case ENT_SOURCE_FILE:
 592+ /* write file */
 593+ //entity->_he_tobuf = entity->_he_frombuf = NULL;
 594+
 595+ /*
 596+ * Compressed data can't be written using sendfile
 597+ */
 598+ entity->_he_state = ENTITY_STATE_SEND_BODY;
 599+
 600+ if (entity->he_encoding) {
 601+ bufferevent_enable(entity->_he_tobuf, EV_WRITE);
 602+ /* The write handler does this for us */
 603+ return;
 604+ }
 605+
 606+ bufferevent_disable(entity->_he_tobuf, EV_WRITE);
 607+
 608+ if (wnet_sendfile(entity->_he_target->fde_fd, entity->he_source.fd.fd,
 609+ entity->he_source.fd.size - entity->he_source.fd.off,
 610+ entity->he_source.fd.off, entity_send_file_done, entity, 0) == -1) {
 611+ entity->_he_func(entity, entity->_he_cbdata, -1);
 612+ }
 613+ return;
 614+ }
 615+ entity->_he_state = ENTITY_STATE_SEND_BODY;
 616+ }
 617+
 618+ if (entity->_he_state == ENTITY_STATE_SEND_BUF) {
 619+ /*
 620+ * Writing buffer completed.
 621+ */
 622+ bufferevent_disable(entity->_he_tobuf, EV_WRITE);
 623+ bufferevent_disable(entity->_he_frombuf, EV_READ);
 624+ entity->_he_func(entity, entity->_he_cbdata, 0);
 625+ return;
 626+ }
 627+
 628+ if (entity->he_source_type == ENT_SOURCE_FILE) {
 629+ /*
 630+ * We only get here if we're writing deflate data.
 631+ */
 632+ ssize_t i;
 633+
 634+ WDEBUG((WLOG_DEBUG, "write file, eof=%d, size=%d, off=%d",
 635+ entity->he_flags.eof, entity->he_source.fd.size,
 636+ entity->he_source.fd.off));
 637+
 638+ if (entity->he_flags.eof) {
 639+ bufferevent_disable(entity->_he_tobuf, EV_WRITE);
 640+ entity->_he_func(entity, entity->_he_cbdata, 0);
 641+ return;
 642+ }
 643+
 644+ if ((i = read(entity->he_source.fd.fd, fbuf, sizeof fbuf)) == -1) {
 645+ bufferevent_disable(entity->_he_tobuf, EV_WRITE);
 646+ entity->_he_func(entity, entity->_he_cbdata, -1);
 647+ return;
 648+ }
 649+ if (write_data(entity, fbuf, i) == -1) {
 650+ bufferevent_disable(entity->_he_tobuf, EV_WRITE);
 651+ entity->_he_func(entity, entity->_he_cbdata, -1);
 652+ return;
 653+ }
 654+ entity->he_source.fd.off += i;
 655+ if (entity->he_source.fd.off == (off_t) entity->he_source.fd.size) {
 656+ entity->he_flags.eof = 1;
 657+ write_zlib_eof(entity);
 658+ return;
 659+ }
 660+
 661+ return;
 662+ }
 663+
 664+ WDEBUG((WLOG_DEBUG, "entity_send_target_write: FDE"));
 665+
 666+ /*
 667+ * Otherwise, we're sending from an FDE, and the last write completed.
 668+ */
 669+ bufferevent_enable(entity->_he_frombuf, EV_READ);
 670+ bufferevent_disable(entity->_he_tobuf, EV_WRITE);
 671+ if (!entity->he_flags.drained) {
 672+ entity->he_flags.drained = 1;
 673+ entity_read_callback(entity->_he_frombuf, entity);
 674+ }
 675+}
 676+
 677+static void
 678+entity_send_target_error(struct bufferevent *buf, short err, void *d)
 679+{
 680+struct http_entity *entity = (http_entity *)d;
 681+
 682+ /*
 683+ * Writing to target produced an error.
 684+ */
 685+ entity->_he_func(entity, entity->_he_cbdata, -1);
 686+}
 687+
 688+/*ARGSUSED*/
 689+static void
 690+entity_send_file_done(fde *fde, void *data, int res)
 691+{
 692+struct http_entity *entity = (http_entity *)data;
 693+
 694+ WDEBUG((WLOG_DEBUG, "entity_send_file_done: called for %d [%s], res=%d",
 695+ fde->fde_fd, fde->fde_desc, res));
 696+ entity->_he_func(entity, entity->_he_cbdata, res);
 697+ return;
 698+}
 699+
 700+static int
 701+validhost(const char *host)
 702+{
 703+ for (; *host; ++host) {
 704+ if (!(char_table[(unsigned char)*host] & CHAR_HOST))
 705+ return 0;
 706+ }
 707+ return 1;
 708+}
 709+
 710+static int
 711+via_includes_me(const char *s)
 712+{
 713+ char *orig = wstrdup(s);
 714+ char *via = orig, *comma, *space;
 715+
 716+ do {
 717+ comma = strchr(via, ',');
 718+ if (comma)
 719+ *comma++ = '\0';
 720+ via = strchr(via, ' ');
 721+ if (!via)
 722+ break;
 723+ while (*via == ' ')
 724+ ++via;
 725+ space = strchr(via, ' ');
 726+ if (!space) {
 727+ wfree(orig);
 728+ return 0;
 729+ }
 730+ *space = '\0';
 731+ if (!strcmp(via, my_hostname)) {
 732+ wfree(orig);
 733+ return 1;
 734+ }
 735+ via = comma;
 736+ } while (comma);
 737+ wfree(orig);
 738+ return 0;
 739+}
 740+
 741+#ifdef __lint
 742+# pragma error_messages(off, E_GLOBAL_COULD_BE_STATIC)
 743+#endif
 744+/*
 745+ * Header handling.
 746+ */
 747+void
 748+header_free(header_list *head)
 749+{
 750+struct header_list *next = head->hl_next;
 751+
 752+ while (next) {
 753+ struct header_list *here = next;
 754+ next = here->hl_next;
 755+ wfree((char *)here->hl_name);
 756+ wfree((char *)here->hl_value);
 757+ wfree(here);
 758+ }
 759+
 760+ bzero(head, sizeof(*head));
 761+}
 762+
 763+#ifdef __lint
 764+# pragma error_messages(default, E_GLOBAL_COULD_BE_STATIC)
 765+#endif
 766+
 767+void
 768+header_add(header_list *head, char *name, char *value)
 769+{
 770+struct header_list *n = head;
 771+
 772+ head->hl_num++;
 773+
 774+ if (head->hl_tail)
 775+ n = head->hl_tail;
 776+ else
 777+ while (n->hl_next)
 778+ n = n->hl_next;
 779+ n->hl_next = (header_list *)wmalloc(sizeof(*head->hl_next));
 780+ head->hl_tail = n->hl_next;
 781+ head->hl_len += strlen(name) + strlen(value) + 4;
 782+ n = n->hl_next;
 783+ n->hl_name = name;
 784+ n->hl_value = value;
 785+ n->hl_next = n->hl_tail = NULL;
 786+ n->hl_flags = 0;
 787+}
 788+
 789+void
 790+header_append_last(header_list *head, const char *append)
 791+{
 792+struct header_list *last = head;
 793+ char *cur;
 794+
 795+ assert(last->hl_next);
 796+
 797+ while (last->hl_next)
 798+ last = last->hl_next;
 799+
 800+ cur = last->hl_value;
 801+ last->hl_value = (char *)wmalloc(strlen(cur) + 1 + strlen(append) + 1);
 802+ sprintf(last->hl_value, "%s %s", cur, append);
 803+ wfree(cur);
 804+}
 805+
 806+
 807+void
 808+header_remove(header_list *head, header_list *it)
 809+{
 810+struct header_list *jt;
 811+
 812+ jt = head;
 813+ while (jt->hl_next && jt->hl_next != it)
 814+ jt = jt->hl_next;
 815+ jt->hl_next = jt->hl_next->hl_next;
 816+ if (it == head->hl_tail)
 817+ head->hl_tail = jt;
 818+ wfree(it->hl_name);
 819+ wfree(it->hl_value);
 820+ wfree(it);
 821+}
 822+
 823+struct header_list *
 824+header_find(header_list *head, const char *name)
 825+{
 826+struct header_list *it;
 827+
 828+ for (it = head->hl_next; it; it = it->hl_next)
 829+ if (!strcasecmp(name, it->hl_name))
 830+ return it;
 831+ return NULL;
 832+}
 833+
 834+#ifdef __lint
 835+# pragma error_messages(off, E_GLOBAL_COULD_BE_STATIC)
 836+#endif
 837+char *
 838+header_build(header_list *head)
 839+{
 840+ char *buf;
 841+ size_t bufsz;
 842+ size_t buflen = 0;
 843+
 844+ bufsz = head->hl_len + 3;
 845+ if ((buf = (char *)wmalloc(bufsz)) == NULL)
 846+ outofmemory();
 847+
 848+ *buf = '\0';
 849+ while (head->hl_next) {
 850+ head = head->hl_next;
 851+ buflen += snprintf(buf + buflen, bufsz - buflen - 1, "%s: %s\r\n", head->hl_name, head->hl_value);
 852+ }
 853+ if (strlcat(buf, "\r\n", bufsz) >= bufsz)
 854+ abort();
 855+
 856+ return buf;
 857+}
 858+#ifdef __lint
 859+# pragma error_messages(default, E_GLOBAL_COULD_BE_STATIC)
 860+#endif
 861+
 862+void
 863+header_dump(header_list *head, int fd)
 864+{
 865+ int i = 0;
 866+struct header_list *h;
 867+
 868+ h = head->hl_next;
 869+ while (h) {
 870+ h = h->hl_next;
 871+ ++i;
 872+ }
 873+
 874+ write(fd, &i, sizeof(i));
 875+
 876+ while (head->hl_next) {
 877+ int i, j;
 878+ head = head->hl_next;
 879+ i = strlen(head->hl_name);
 880+ write(fd, &i, sizeof(i));
 881+ j = strlen(head->hl_value);
 882+ write(fd, &j, sizeof(j));
 883+ write(fd, head->hl_name, i);
 884+ write(fd, head->hl_value, j);
 885+ }
 886+}
 887+
 888+int
 889+header_undump(header_list *head, int fd, off_t *len)
 890+{
 891+ int i = 0, j = 0, n = 0;
 892+struct header_list *it = head;
 893+ ssize_t r;
 894+
 895+ *len = 0;
 896+ bzero(head, sizeof(*head));
 897+ if ((r = read(fd, &n, sizeof(n))) < 0) {
 898+ wlog(WLOG_WARNING, "reading cache file: %s", strerror(errno));
 899+ return -1; /* XXX */
 900+ }
 901+
 902+ *len += r;
 903+ WDEBUG((WLOG_DEBUG, "header_undump: %d entries", n));
 904+
 905+ while (n--) {
 906+ char *n, *v, *s;
 907+ int k;
 908+
 909+ if ((it->hl_next = (header_list *)wcalloc(1, sizeof(struct header_list))) == NULL)
 910+ outofmemory();
 911+ it = it->hl_next;
 912+ *len += read(fd, &i, sizeof(i));
 913+ *len += read(fd, &j, sizeof(j));
 914+ WDEBUG((WLOG_DEBUG, "header_undump: i=%d j=%d", i, j));
 915+ n = (char *)wmalloc(i + j + 2);
 916+ i = read(fd, n, i);
 917+ *len += i;
 918+ s = n + i;
 919+ *s++ = '\0';
 920+ v = s;
 921+ k = read(fd, s, j);
 922+ *len += k;
 923+ s += k;
 924+ *s = '\0';
 925+ it->hl_name = n;
 926+ it->hl_value = wstrdup(v);
 927+ head->hl_len += i + j + 4;
 928+ }
 929+
 930+ head->hl_tail = it;
 931+ return 0;
 932+}
 933+
 934+static int
 935+parse_headers(http_entity *entity)
 936+{
 937+ char *line;
 938+
 939+ while ((line = evbuffer_readline(entity->_he_frombuf->input)) != NULL) {
 940+ char **hdr = NULL;
 941+ char *value = NULL;
 942+ int error = 1;
 943+
 944+ if (!line)
 945+ return 0;
 946+
 947+ if (!*line) {
 948+ if (!entity->he_reqstr) {
 949+ free(line);
 950+ return ENT_ERR_INVREQ;
 951+ }
 952+
 953+ entity->_he_state = ENTITY_STATE_DONE;
 954+ free(line);
 955+ return 0;
 956+ }
 957+
 958+ switch(entity->_he_state) {
 959+ case ENTITY_STATE_START:
 960+ entity->he_reqstr = wstrdup(line);
 961+ if (parse_reqtype(entity) == -1) {
 962+ free(line);
 963+ return ENT_ERR_INVREQ;
 964+ }
 965+ entity->_he_state = ENTITY_STATE_HDR;
 966+ break;
 967+ case ENTITY_STATE_HDR:
 968+ if (isspace(*line)) {
 969+ char *s = line;
 970+ if (!entity->he_headers.hl_next) {
 971+ error = -1;
 972+ goto error;
 973+ }
 974+ while (isspace(*s))
 975+ s++;
 976+ header_append_last(&entity->he_headers, s);
 977+ free(line);
 978+ continue;
 979+ }
 980+
 981+ hdr = wstrvec(line, ":", 2);
 982+
 983+ if (!hdr[0] || !hdr[1]) {
 984+ error = ENT_ERR_INVREQ;
 985+ goto error;
 986+ }
 987+
 988+ value = hdr[1];
 989+ while (isspace(*value))
 990+ ++value;
 991+ value = wstrdup(value);
 992+
 993+ WDEBUG((WLOG_DEBUG, "header: from [%s], [%s] = [%s]",
 994+ line, hdr[0], value));
 995+
 996+ if (++entity->he_headers.hl_num > MAX_HEADERS) {
 997+ error = ENT_ERR_2MANY;
 998+ goto error;
 999+ }
 1000+ if (!strcasecmp(hdr[0], "Host")) {
 1001+ if (!validhost(value)) {
 1002+ error = ENT_ERR_INVHOST;
 1003+ goto error;
 1004+ }
 1005+ header_add(&entity->he_headers, wstrdup(hdr[0]), value);
 1006+ entity->he_rdata.request.host = wstrdup(value);
 1007+ } else if (!strcasecmp(hdr[0], "Content-Length")) {
 1008+ header_add(&entity->he_headers, wstrdup(hdr[0]), value);
 1009+ entity->he_rdata.request.contlen = atoi(value);
 1010+ } else if (!strcasecmp(hdr[0], "Via")) {
 1011+ if (via_includes_me(value)) {
 1012+ error = ENT_ERR_LOOP;
 1013+ goto error;
 1014+ }
 1015+ header_add(&entity->he_headers, wstrdup(hdr[0]), value);
 1016+ } else if (!strcasecmp(hdr[0], "transfer-encoding")) {
 1017+ /* XXX */
 1018+ if (!strcasecmp(value, "chunked")) {
 1019+ entity->he_te |= TE_CHUNKED;
 1020+ }
 1021+ /* Don't forward transfer-encoding... */
 1022+ wfree(value);
 1023+ } else if (!strcasecmp(hdr[0], "Accept-Encoding")) {
 1024+ if (!entity->he_flags.response &&
 1025+ qvalue_parse(&entity->he_rdata.request.accept_encoding, value) == -1) {
 1026+ error = -1;
 1027+ WDEBUG((WLOG_DEBUG, "a-e parse failed"));
 1028+ goto error;
 1029+ }
 1030+ } else if (!strcasecmp(hdr[0], "Pragma")) {
 1031+ entity->he_h_pragma = wstrdup(value);
 1032+ header_add(&entity->he_headers, wstrdup(hdr[0]), entity->he_h_pragma);
 1033+ } else if (!strcasecmp(hdr[0], "Cache-Control")) {
 1034+ entity->he_h_cache_control = wstrdup(value);
 1035+ header_add(&entity->he_headers, wstrdup(hdr[0]), entity->he_h_cache_control);
 1036+ } else if (!strcasecmp(hdr[0], "If-Modified-Since")) {
 1037+ entity->he_h_if_modified_since = wstrdup(value);
 1038+ header_add(&entity->he_headers, wstrdup(hdr[0]), entity->he_h_if_modified_since);
 1039+ } else if (!strcasecmp(hdr[0], "Transfer-Encoding")) {
 1040+ entity->he_h_transfer_encoding = wstrdup(value);
 1041+ header_add(&entity->he_headers, wstrdup(hdr[0]), entity->he_h_transfer_encoding);
 1042+ } else if (!strcasecmp(hdr[0], "Last-Modified")) {
 1043+ entity->he_h_last_modified = wstrdup(value);
 1044+ header_add(&entity->he_headers, wstrdup(hdr[0]), entity->he_h_last_modified);
 1045+ } else
 1046+ header_add(&entity->he_headers, wstrdup(hdr[0]), value);
 1047+
 1048+ wstrvecfree(hdr);
 1049+ break;
 1050+ error:
 1051+ if (hdr)
 1052+ wstrvecfree(hdr);
 1053+ if (value)
 1054+ wfree(value);
 1055+ free(line);
 1056+ return -1;
 1057+ }
 1058+
 1059+ free(line);
 1060+ }
 1061+ return 0;
 1062+}
 1063+
 1064+static int
 1065+parse_reqtype(http_entity *entity)
 1066+{
 1067+ char *p, *s, *t;
 1068+ char *request = entity->he_reqstr;;
 1069+ int i;
 1070+
 1071+ WDEBUG((WLOG_DEBUG, "parse_reqtype: called, response=%d", (int)entity->he_flags.response));
 1072+
 1073+ /*
 1074+ * These probably shouldn't be handled in the same function.
 1075+ */
 1076+ if (entity->he_flags.response) {
 1077+ /*
 1078+ * HTTP/1.0
 1079+ */
 1080+ if ((p = strchr(request, ' ')) == NULL)
 1081+ return -1;
 1082+ *p++ = '\0';
 1083+
 1084+ /* 200 */
 1085+ if ((s = strchr(p, ' ')) == NULL)
 1086+ return -1;
 1087+ *s++ = '\0';
 1088+ entity->he_rdata.response.status = atoi(p);
 1089+
 1090+ /* OK */
 1091+ entity->he_rdata.response.status_str = s;
 1092+
 1093+ WDEBUG((WLOG_DEBUG, "parse_reqtype: \"%s\" \"%d\" \"%s\"",
 1094+ request, entity->he_rdata.response.status,
 1095+ entity->he_rdata.response.status_str));
 1096+ return 0;
 1097+ }
 1098+
 1099+ /* GET */
 1100+ if ((p = strchr(request, ' ')) == NULL)
 1101+ return -1;
 1102+
 1103+ *p++ = '\0';
 1104+
 1105+ for (i = 0; supported_reqtypes[i].name; i++)
 1106+ if (!strcmp(request, supported_reqtypes[i].name))
 1107+ break;
 1108+
 1109+ entity->he_rdata.request.reqtype = supported_reqtypes[i].type;
 1110+ if (entity->he_rdata.request.reqtype == REQTYPE_INVALID)
 1111+ return -1;
 1112+
 1113+ /* /path/to/file */
 1114+ if ((s = strchr(p, ' ')) == NULL)
 1115+ return -1;
 1116+
 1117+ *s++ = '\0';
 1118+ if (*p != '/') {
 1119+ /*
 1120+ * This normally means the request URI was of the form
 1121+ * "http://host.tld/file". Clients don't send this, but
 1122+ * Squid does when it thinks we're a proxy.
 1123+ *
 1124+ * Extract the host and set it now. If there's another host
 1125+ * later, it'll overwrite it, but if the client sends two
 1126+ * different hosts it's probably broken anyway...
 1127+ *
 1128+ * We could handle non-http URIs here, but there's not much
 1129+ * points, the backend will reject it anyway.
 1130+ */
 1131+ if (strncmp(p, "http://", 7))
 1132+ return -1;
 1133+ p += 7;
 1134+ t = strchr(p, '/');
 1135+ if (t == NULL)
 1136+ return -1;
 1137+ entity->he_rdata.request.path = wstrdup(t);
 1138+ *t = '\0';
 1139+ entity->he_rdata.request.host = p;
 1140+ } else {
 1141+ entity->he_rdata.request.path = wstrdup(p);
 1142+ }
 1143+
 1144+ /* HTTP/1.0 */
 1145+ if (sscanf(s, "HTTP/%d.%d", &entity->he_rdata.request.httpmaj,
 1146+ &entity->he_rdata.request.httpmin) != 2)
 1147+ return -1;
 1148+
 1149+ return 0;
 1150+}
 1151+
 1152+int
 1153+qvalue_parse(qvalue_head *list, const char *header)
 1154+{
 1155+ char **values;
 1156+ char **value;
 1157+
 1158+ TAILQ_INIT(list);
 1159+
 1160+ values = wstrvec(header, ",", 0);
 1161+ for (value = values; *value; ++value) {
 1162+ char **bits;
 1163+ struct qvalue *entry;
 1164+ bits = wstrvec(*value, ";", 0);
 1165+ entry = (qvalue *)wcalloc(1, sizeof(*entry));
 1166+ entry->name = wstrdup(bits[0]);
 1167+ if (bits[1])
 1168+ entry->val = atof(bits[1]);
 1169+ else
 1170+ entry->val = 1.0;
 1171+ TAILQ_INSERT_TAIL(list, entry, entries);
 1172+ wstrvecfree(bits);
 1173+ }
 1174+
 1175+ wstrvecfree(values);
 1176+ return 0;
 1177+}
 1178+
 1179+struct qvalue *
 1180+qvalue_remove_best(qvalue_head *list)
 1181+{
 1182+ struct qvalue *entry, *best = NULL;
 1183+ float bestf = 0;
 1184+ TAILQ_FOREACH(entry, list, entries) {
 1185+ if (entry->val > bestf) {
 1186+ best = entry;
 1187+ bestf = entry->val;
 1188+ }
 1189+ }
 1190+ if (!best)
 1191+ return NULL;
 1192+ TAILQ_REMOVE(list, best, entries);
 1193+ return best;
 1194+}
 1195+
 1196+enum encoding
 1197+accept_encoding(const char *enc)
 1198+{
 1199+static struct {
 1200+ const char *name;
 1201+ enum encoding value;
 1202+} encs[] = {
 1203+ { "deflate", E_DEFLATE },
 1204+ { "x-deflate", E_X_DEFLATE },
 1205+ { "identity", E_NONE },
 1206+ { "gzip", E_GZIP },
 1207+ { "x-gzip", E_X_GZIP },
 1208+ { NULL, E_NONE }
 1209+}, *s;
 1210+ for (s = encs; s->name; s++)
 1211+ if (!strcasecmp(s->name, enc))
 1212+ return s->value;
 1213+ return E_NONE;
 1214+}
Index: trunk/willow/src/willow/wnet_libevent.cc
@@ -0,0 +1,124 @@
 2+/* @(#) $Header$ */
 3+/* This source code is in the public domain. */
 4+/*
 5+ * Willow: Lightweight HTTP reverse-proxy.
 6+ * wnet: Networking.
 7+ */
 8+
 9+#if defined __SUNPRO_C || defined __DECC || defined __HP_cc
 10+# pragma ident "@(#)$Header$"
 11+#endif
 12+
 13+#define _XOPEN_SOURCE 600
 14+#define _XOPEN_SOURCE_EXTENDED
 15+#define __EXTENSIONS__
 16+#ifndef _GNU_SOURCE
 17+# define _GNU_SOURCE
 18+#endif
 19+
 20+#include <sys/types.h>
 21+#include <sys/socket.h>
 22+#include <sys/time.h>
 23+
 24+#include <arpa/inet.h>
 25+
 26+#include <stdio.h>
 27+#include <string.h>
 28+#include <stdlib.h>
 29+#include <unistd.h>
 30+#include <errno.h>
 31+#include <assert.h>
 32+#include <fcntl.h>
 33+#include <signal.h>
 34+#include <event.h>
 35+
 36+#include "willow.h"
 37+#include "wnet.h"
 38+#include "wconfig.h"
 39+#include "wlog.h"
 40+#include "whttp.h"
 41+
 42+struct event ev_sigint;
 43+
 44+static void fde_ev_callback(int, short, void *);
 45+
 46+void sig_exit(int, short, void *);
 47+
 48+void sig_exit(int sig, short what, void *d)
 49+{
 50+ exit(0);
 51+}
 52+
 53+void
 54+wnet_init_select(void)
 55+{
 56+ int i;
 57+
 58+ signal(SIGPIPE, SIG_IGN);
 59+ event_init();
 60+
 61+ signal_set(&ev_sigint, SIGINT, sig_exit, NULL);
 62+ signal_add(&ev_sigint, NULL);
 63+}
 64+
 65+void
 66+wnet_run(void)
 67+{
 68+ event_dispatch();
 69+ perror("event_dispatch");
 70+}
 71+
 72+static void
 73+fde_ev_callback(int fd, short ev, void *d)
 74+{
 75+struct fde *fde = &fde_table[fd];
 76+
 77+ assert(fde->fde_flags.open);
 78+
 79+ if ((ev & EV_READ) && fde->fde_read_handler)
 80+ fde->fde_read_handler(fde);
 81+ if ((ev & EV_WRITE) && fde->fde_write_handler)
 82+ fde->fde_write_handler(fde);
 83+ if (fde->fde_read_handler || fde->fde_write_handler)
 84+ event_add(&fde->fde_ev, NULL);
 85+}
 86+
 87+void
 88+wnet_register(int fd, int what, fdcb handler, void *data)
 89+{
 90+struct fde *fde = &fde_table[fd];
 91+ int ev_flags = 0;
 92+
 93+ if (fde->fde_flags.held)
 94+ return;
 95+
 96+ if (event_pending(&fde->fde_ev, EV_READ | EV_WRITE, NULL))
 97+ event_del(&fde->fde_ev);
 98+
 99+ assert(fde->fde_flags.open);
 100+
 101+ if (what & FDE_READ) {
 102+ fde->fde_read_handler = handler;
 103+ ev_flags |= EV_READ;
 104+ }
 105+ if (what & FDE_WRITE) {
 106+ ev_flags |= EV_WRITE;
 107+ fde->fde_write_handler = handler;
 108+ }
 109+
 110+ if (handler == NULL) {
 111+ //if (event_pending(&fde->fde_ev, EV_READ | EV_WRITE, NULL))
 112+ //if (fde->fde_flags.pend)
 113+ // event_del(fde->fde_ev);
 114+ fde->fde_flags.pend = 0;
 115+ return;
 116+ }
 117+
 118+ if (data)
 119+ fde->fde_rdata = data;
 120+
 121+ //ev_flags |= EV_PERSIST;
 122+ event_set(&fde->fde_ev, fde->fde_fd, ev_flags, fde_ev_callback, fde);
 123+ event_add(&fde->fde_ev, NULL);
 124+ fde->fde_flags.pend = 1;
 125+}
Index: trunk/willow/src/willow/whttp.cc
@@ -0,0 +1,823 @@
 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+#ifndef _GNU_SOURCE
 18+# define _GNU_SOURCE /* glibc strptime */
 19+#endif
 20+
 21+#include <sys/types.h>
 22+#include <sys/stat.h>
 23+#include <sys/param.h>
 24+
 25+#include <stdlib.h>
 26+#include <stdio.h>
 27+#include <string.h>
 28+#include <unistd.h>
 29+#include <errno.h>
 30+#include <netdb.h>
 31+#include <fcntl.h>
 32+#include <strings.h>
 33+#include <assert.h>
 34+#include <time.h>
 35+
 36+#include "willow.h"
 37+#include "whttp.h"
 38+#include "wnet.h"
 39+#include "wbackend.h"
 40+#include "wconfig.h"
 41+#include "wlogwriter.h"
 42+#include "whttp_entity.h"
 43+#include "wlog.h"
 44+#include "wcache.h"
 45+
 46+#ifndef MAXHOSTNAMELEN
 47+# define MAXHOSTNAMELEN HOST_NAME_MAX /* SysV / BSD disagreement */
 48+#endif
 49+
 50+/*
 51+ * Error handling.
 52+ */
 53+#define ERR_GENERAL 0
 54+#define ERR_BADREQUEST 1
 55+#define ERR_BADRESPONSE 2
 56+#define ERR_CACHE_IO 3
 57+
 58+static const char *error_files[] = {
 59+ /* ERR_GENERAL */ DATADIR "/errors/ERR_GENERAL",
 60+ /* ERR_BADREQUEST */ DATADIR "/errors/ERR_BADREQUEST",
 61+ /* ERR_BADRESPONSE */ DATADIR "/errors/ERR_BADRESPONSE",
 62+ /* ERR_CACHE_IO */ DATADIR "/errors/ERR_CACHE_IO",
 63+};
 64+
 65+const char *request_string[] = {
 66+ "GET",
 67+ "POST",
 68+ "HEAD",
 69+ "TRACE",
 70+ "OPTIONS",
 71+};
 72+
 73+struct request_type supported_reqtypes[] = {
 74+ { "GET", 3, REQTYPE_GET },
 75+ { "POST", 4, REQTYPE_POST },
 76+ { "HEAD", 4, REQTYPE_HEAD },
 77+ { "TRACE", 5, REQTYPE_TRACE },
 78+ { "OPTIONS", 7, REQTYPE_OPTIONS },
 79+ { NULL, 0, REQTYPE_INVALID }
 80+};
 81+
 82+struct http_client {
 83+struct fde *cl_fde; /* backref to fd */
 84+ int cl_reqtype; /* request type or 0 */
 85+ char *cl_path; /* path they want */
 86+ char *cl_wrtbuf; /* write buf (either to client or be) */
 87+struct backend *cl_backend; /* backend servicing this client */
 88+struct fde *cl_backendfde; /* fde for backend */
 89+struct http_entity cl_entity; /* reply to send back */
 90+
 91+ /* Cache-related data */
 92+ int cl_cfd; /* FD of cache file for writing, or 0 */
 93+struct cache_object *cl_co; /* Cache object */
 94+ struct {
 95+ int f_cached:1;
 96+ int f_closed:1;
 97+ int f_http11:1; /* Client understands HTTP/1.1 */
 98+ } cl_flags;
 99+ size_t cl_dsize; /* Object size */
 100+enum encoding cl_enc;
 101+struct http_client *fe_next; /* freelist */
 102+};
 103+
 104+static struct http_client freelist;
 105+static struct http_client deadlist;
 106+
 107+static void client_close(struct http_client *);
 108+static void proxy_start_backend(struct backend *, struct fde *, void *);
 109+static void client_read_done(struct http_entity *, void *, int);
 110+static void client_response_done(struct http_entity *, void *, int);
 111+static void backend_headers_done(struct http_entity *, void *, int);
 112+static void client_headers_done(struct http_entity *, void *, int);
 113+static void client_write_cached(struct http_client *);
 114+static int removable_header(const char *);
 115+
 116+static void client_send_error(struct http_client *, int errcode, const char *error,
 117+ int status, const char *statusstr);
 118+static void client_log_request(struct http_client *);
 119+
 120+static void do_cache_write(const char *, size_t, void *);
 121+
 122+static char via_hdr[1024];
 123+static char *cache_hit_hdr;
 124+static char *cache_miss_hdr;
 125+
 126+char my_hostname[MAXHOSTNAMELEN + 1];
 127+static char my_version[64];
 128+static int logwr_pipe[2];
 129+static FILE *alf;
 130+
 131+/*
 132+ * Initialize whttp, start loggers.
 133+ */
 134+void
 135+whttp_init(void)
 136+{
 137+ int hsize;
 138+
 139+ if (gethostname(my_hostname, MAXHOSTNAMELEN) < 0) {
 140+ perror("gethostname");
 141+ exit(8);
 142+ }
 143+
 144+ (void)strlcpy(my_version, "Willow/" PACKAGE_VERSION, 64);
 145+ snprintf(via_hdr, sizeof(via_hdr), "1.1 %s (%s)", my_hostname, my_version);
 146+
 147+ hsize = sizeof("MISS from ") + strlen(my_hostname);
 148+ cache_hit_hdr = (char *)wmalloc(hsize + 1);
 149+ cache_miss_hdr = (char *)wmalloc(hsize + 1);
 150+
 151+ if (cache_hit_hdr == NULL || cache_miss_hdr == NULL)
 152+ outofmemory();
 153+
 154+ snprintf(cache_hit_hdr, hsize, "HIT from %s", my_hostname);
 155+ snprintf(cache_miss_hdr, hsize, "MISS from %s", my_hostname);
 156+
 157+ /*
 158+ * Fork the logwriter.
 159+ */
 160+
 161+ if (config.access_log.size()) {
 162+ if (pipe(logwr_pipe) < 0) {
 163+ perror("pipe");
 164+ exit(8);
 165+ }
 166+ switch (fork()) {
 167+ case -1:
 168+ wlog(WLOG_ERROR, "starting logwriter: %s", strerror(errno));
 169+ exit(8);
 170+ /*NOTREACHED*/
 171+ case 0:
 172+ (void)dup2(logwr_pipe[0], STDIN_FILENO);
 173+ /*LINTED execl*/
 174+ (void)execl(LIBEXECDIR "/wlogwriter", "wlogwriter",
 175+ config.access_log.c_str(), NULL);
 176+ exit(8);
 177+ /*NOTREACHED*/
 178+ default:
 179+ break;
 180+ }
 181+ if ((alf = fdopen(logwr_pipe[1], "a")) == NULL) {
 182+ perror("whttp: fdopen");
 183+ exit(8);
 184+ }
 185+ }
 186+}
 187+
 188+void
 189+whttp_shutdown(void)
 190+{
 191+ wfree(cache_hit_hdr);
 192+ wfree(cache_miss_hdr);
 193+}
 194+
 195+/*
 196+ * Create a new client associated with the FDE 'e'.
 197+ */
 198+static http_client *
 199+new_client(fde *e)
 200+{
 201+struct http_client *cl;
 202+
 203+ if (freelist.fe_next) {
 204+ cl = freelist.fe_next;
 205+ freelist.fe_next = cl->fe_next;
 206+ new (cl) http_client;
 207+ } else
 208+ cl = new http_client;
 209+
 210+ memset(cl, 0, sizeof(*cl));
 211+ cl->cl_fde = e;
 212+ return cl;
 213+}
 214+
 215+/*
 216+ * Called by wnet_accept to regiister a new client. Reads the request headers
 217+ * from the client.
 218+ */
 219+void
 220+http_new(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(http_entity *entity, void *data, int res)
 241+{
 242+struct http_client *client = (http_client *)data;
 243+struct cache_object *cobj;
 244+ char *pragma, *cache_control, *ifmod;
 245+struct qvalue_head *acceptenc;
 246+struct qvalue *val;
 247+ int cacheable = 1;
 248+
 249+ WDEBUG((WLOG_DEBUG, "client_read_done: called, res=%d", res));
 250+
 251+ if (res < -1) {
 252+ client_send_error(client, ERR_BADREQUEST, ent_errors[-res], 400, "Bad request (#10.4.1)");
 253+ return;
 254+ } else if (res == -1) {
 255+ client_close(client);
 256+ return;
 257+ } else if (res == 1) {
 258+ client_close(client);
 259+ return;
 260+ }
 261+
 262+ if (client->cl_entity.he_rdata.request.httpmaj >= 1 &&
 263+ client->cl_entity.he_rdata.request.httpmin >= 1)
 264+ client->cl_flags.f_http11 = 1;
 265+
 266+ if (client->cl_entity.he_rdata.request.host == NULL)
 267+ client->cl_path = wstrdup(client->cl_entity.he_rdata.request.path);
 268+ else {
 269+ int len;
 270+
 271+ len = strlen(client->cl_entity.he_rdata.request.host) +
 272+ strlen(client->cl_entity.he_rdata.request.path) + 7;
 273+
 274+ client->cl_path = (char *)wmalloc(len + 1);
 275+ if (client->cl_path == NULL)
 276+ outofmemory();
 277+ snprintf(client->cl_path, len + 1, "http://%s%s",
 278+ client->cl_entity.he_rdata.request.host,
 279+ client->cl_entity.he_rdata.request.path);
 280+ }
 281+
 282+ client->cl_reqtype = client->cl_entity.he_rdata.request.reqtype;
 283+
 284+ pragma = entity->he_h_pragma;
 285+ cache_control = entity->he_h_cache_control;
 286+
 287+ if (pragma) {
 288+ char **pragmas = wstrvec(pragma, ",", 0);
 289+ char **s;
 290+ for (s = pragmas; *s; ++s) {
 291+ if (!strcasecmp(*s, "no-cache")) {
 292+ cacheable = 0;
 293+ break;
 294+ }
 295+ }
 296+ wstrvecfree(pragmas);
 297+ }
 298+
 299+ if (cache_control) {
 300+ char **cache_controls = wstrvec(cache_control, ",", 0);
 301+ char **s;
 302+ for (s = cache_controls; *s; ++s) {
 303+ if (!strcasecmp(*s, "no-cache")) {
 304+ cacheable = 0;
 305+ break;
 306+ }
 307+ }
 308+ wstrvecfree(cache_controls);
 309+ }
 310+
 311+ acceptenc = &entity->he_rdata.request.accept_encoding;
 312+ while ((val = qvalue_remove_best(acceptenc)) != NULL) {
 313+ WDEBUG((WLOG_DEBUG, "client offers [%s] q=%f", val->name, (double) val->val));
 314+ if ((client->cl_enc = accept_encoding(val->name)) != E_NONE) {
 315+ wfree(val);
 316+ break;
 317+ }
 318+ wfree(val);
 319+ }
 320+
 321+ /*
 322+ * Check for cached object.
 323+ */
 324+ if (config.ncaches && client->cl_reqtype == REQTYPE_GET) {
 325+ if (cacheable)
 326+ client->cl_co = wcache_find_object(client->cl_path, &client->cl_cfd,
 327+ WCACHE_RDWR);
 328+ else
 329+ client->cl_co = wcache_find_object(client->cl_path, &client->cl_cfd,
 330+ WCACHE_WRONLY);
 331+
 332+ if (cacheable && client->cl_co && client->cl_co->co_time &&
 333+ (ifmod = entity->he_h_if_modified_since)) {
 334+ char *s;
 335+ time_t t;
 336+ struct tm m;
 337+ s = strptime(ifmod, "%a, %d %b %Y %H:%M:%S", &m);
 338+ if (s) {
 339+ t = mktime(&m);
 340+ WDEBUG((WLOG_DEBUG, "if-mod: %d, last-mod: %d", t, client->cl_co->co_time));
 341+ if (t >= client->cl_co->co_time) {
 342+ /*
 343+ * Not modified
 344+ */
 345+ client_send_error(client, -1, NULL, 304, "Not modified (#10.3.5)");
 346+ return;
 347+ }
 348+ }
 349+ }
 350+
 351+ if (client->cl_co && client->cl_co->co_complete) {
 352+ WDEBUG((WLOG_DEBUG, "client_read_done: object %s cached", client->cl_path));
 353+ client_write_cached(client);
 354+ return;
 355+ }
 356+ WDEBUG((WLOG_DEBUG, "client_read_done: %s not cached", client->cl_path));
 357+ }
 358+
 359+ /*
 360+ * Not cached. Find a backend.
 361+ */
 362+ if (get_backend(client->cl_path, proxy_start_backend, client, 0) == -1) {
 363+ client_send_error(client, ERR_GENERAL,
 364+ "No backends were available to service your request", 503,
 365+ "Service unavailable (#10.5.4)");
 366+ return;
 367+ }
 368+}
 369+
 370+/*
 371+ * Called when backend is ready. backend==NULL if none was found.
 372+ */
 373+static void
 374+proxy_start_backend(backend *backend, fde *e, void *data)
 375+{
 376+struct http_client *client = (http_client *)data;
 377+struct header_list *it;
 378+ int error = 0;
 379+ socklen_t len = sizeof(error);
 380+
 381+ WDEBUG((WLOG_DEBUG, "proxy_start_backend: called; for client=%d", client->cl_fde->fde_fd));
 382+
 383+ if (backend == NULL) {
 384+ client_send_error(client, ERR_GENERAL,
 385+ "No backends were available to service your request",
 386+ 503, "Service unavailable (#10.5.4)");
 387+ return;
 388+ }
 389+
 390+ client->cl_backend = backend;
 391+ client->cl_backendfde = e;
 392+
 393+ getsockopt(e->fde_fd, SOL_SOCKET, SO_ERROR, &error, &len);
 394+ if (error) {
 395+ client_send_error(client, ERR_GENERAL, strerror(error), 503,
 396+ "Service unavailable (#10.5.4)");
 397+ return;
 398+ }
 399+
 400+ for (it = client->cl_entity.he_headers.hl_next; it; it = it->hl_next) {
 401+ if (removable_header(it->hl_name)) {
 402+ header_remove(&client->cl_entity.he_headers, it);
 403+ it = client->cl_entity.he_headers.hl_next;
 404+ continue;
 405+ }
 406+ }
 407+
 408+ header_add(&client->cl_entity.he_headers, wstrdup("X-Forwarded-For"), wstrdup(client->cl_fde->fde_straddr));
 409+ header_add(&client->cl_entity.he_headers, wstrdup("Connection"), wstrdup("Close"));
 410+ /*
 411+ * POST requests require Content-Length.
 412+ */
 413+ if (client->cl_reqtype == REQTYPE_POST) {
 414+ if (client->cl_entity.he_rdata.request.contlen == -1) {
 415+ client_send_error(client, ERR_BADREQUEST, "POST request without Content-Length",
 416+ 411, "Length required (#10.4.12)");
 417+ return;
 418+ }
 419+
 420+ WDEBUG((WLOG_DEBUG, "client content-length=%d", client->cl_entity.he_rdata.request.contlen));
 421+ client->cl_entity.he_source_type = ENT_SOURCE_FDE;
 422+ client->cl_entity.he_source.fde.fde = client->cl_fde;
 423+ client->cl_entity.he_source.fde.len = client->cl_entity.he_rdata.request.contlen;
 424+ } else
 425+ client->cl_entity.he_source_type = ENT_SOURCE_NONE;
 426+
 427+ entity_send(e, &client->cl_entity, backend_headers_done, client, 0);
 428+}
 429+
 430+/*
 431+ * Called when clients request was written to the backend.
 432+ */
 433+/*ARGSUSED*/
 434+static void
 435+backend_headers_done(http_entity *entity, void *data, int res)
 436+{
 437+struct http_client *client = (http_client *)data;
 438+
 439+ WDEBUG((WLOG_DEBUG, "backend_headers_done: called"));
 440+ if (res == -1) {
 441+ client_send_error(client, ERR_GENERAL, strerror(errno), 503,
 442+ "Service unavailable (#10.5.4)");
 443+ return;
 444+ }
 445+
 446+ entity_free(&client->cl_entity);
 447+ bzero(&client->cl_entity, sizeof(client->cl_entity));
 448+ client->cl_entity.he_source_type = ENT_SOURCE_FDE;
 449+ client->cl_entity.he_source.fde.fde = client->cl_backendfde;
 450+ client->cl_entity.he_source.fde.len = -1;
 451+
 452+ entity_set_response(&client->cl_entity, 1);
 453+
 454+ /*
 455+ * This should probably be handled somewhere inside
 456+ * whttp_entity.c ...
 457+ */
 458+ entity_read_headers(&client->cl_entity, client_headers_done, client);
 459+}
 460+
 461+/*
 462+ * Called when backend's headers are finished reading.
 463+ */
 464+static void
 465+client_headers_done(http_entity *entity, void *data, int res)
 466+{
 467+struct http_client *client = (http_client *)data;
 468+ char *cache_path;
 469+ size_t plen;
 470+
 471+ WDEBUG((WLOG_DEBUG, "client_headers_done: called"));
 472+
 473+ if (res == -1) {
 474+ client_close(client);
 475+ return;
 476+ } else if (res < -1) {
 477+ client_send_error(client, ERR_GENERAL, ent_errors[-res], 503,
 478+ "Service unavailable (#10.5.4)");
 479+ return;
 480+ }
 481+
 482+ /*
 483+ * If cachable, open the cache file and write data.
 484+ *
 485+ * Don't cache responses to non-GET requests, or non-200 replies.
 486+ */
 487+ if (client->cl_reqtype != REQTYPE_GET || entity->he_rdata.response.status != 200
 488+ || !config.ncaches || !client->cl_co) {
 489+ if (client->cl_co)
 490+ wcache_release(client->cl_co, 0);
 491+ } else if (client->cl_co) {
 492+ char *lastmod;
 493+
 494+ entity->he_cache_callback = do_cache_write;
 495+ entity->he_cache_callback_data = client;
 496+ header_dump(&client->cl_entity.he_headers, client->cl_cfd);
 497+
 498+ /*
 499+ * Look for last-modified
 500+ */
 501+ if ((lastmod = entity->he_h_last_modified) != NULL) {
 502+ struct tm tim;
 503+ char *res;
 504+ res = strptime(lastmod, "%a, %d %b %Y %H:%M:%S", &tim);
 505+ if (res) {
 506+ WDEBUG((WLOG_DEBUG, "last-modified: %d", mktime(&tim)));
 507+ client->cl_co->co_time = mktime(&tim);
 508+ }
 509+ }
 510+ }
 511+
 512+ header_add(&client->cl_entity.he_headers, wstrdup("Via"), wstrdup(via_hdr));
 513+ header_add(&client->cl_entity.he_headers, wstrdup("X-Cache"), wstrdup(cache_miss_hdr));
 514+ client->cl_entity.he_source.fde.len = -1;
 515+ if (config.compress)
 516+ client->cl_entity.he_encoding = client->cl_enc;
 517+
 518+ if (!HAS_BODY(client->cl_entity.he_rdata.response.status))
 519+ client->cl_entity.he_source_type = ENT_SOURCE_NONE;
 520+
 521+ entity_send(client->cl_fde, &client->cl_entity, client_response_done, client,
 522+ client->cl_flags.f_http11 ? ENT_CHUNKED_OKAY : 0);
 523+}
 524+
 525+/*
 526+ * Write a cached object to the client.
 527+ */
 528+static void
 529+client_write_cached(http_client *client)
 530+{
 531+ size_t plen;
 532+ char *cache_path;
 533+struct stat sb;
 534+ char size[64];
 535+
 536+ plen = strlen(config.caches[0].dir) + client->cl_co->co_plen + 12 + 2;
 537+ if ((cache_path = (char *)wcalloc(1, plen + 1)) == NULL)
 538+ outofmemory();
 539+
 540+ if (fstat(client->cl_cfd, &sb) < 0) {
 541+ wlog(WLOG_WARNING, "stat(%s): %s", cache_path, strerror(errno));
 542+ client_send_error(client, ERR_CACHE_IO, strerror(errno),
 543+ 500, "Internal server error (#10.5.1)");
 544+ wfree(cache_path);
 545+ return;
 546+ }
 547+
 548+ wfree(cache_path);
 549+
 550+ entity_free(&client->cl_entity);
 551+ bzero(&client->cl_entity, sizeof(client->cl_entity));
 552+ header_undump(&client->cl_entity.he_headers, client->cl_cfd, &client->cl_entity.he_source.fd.off);
 553+ header_add(&client->cl_entity.he_headers, wstrdup("Via"), wstrdup(via_hdr));
 554+ header_add(&client->cl_entity.he_headers, wstrdup("X-Cache"), wstrdup(cache_hit_hdr));
 555+ if (!client->cl_enc && !header_find(&client->cl_entity.he_headers, "Content-Length")) {
 556+ snprintf(size, sizeof(size), "%d", client->cl_co->co_size);
 557+ header_add(&client->cl_entity.he_headers, wstrdup("Content-Length"), wstrdup(size));
 558+ }
 559+
 560+ entity_set_response(&client->cl_entity, 1);
 561+ client->cl_entity.he_rdata.response.status = 200;
 562+ client->cl_entity.he_rdata.response.status_str = "OK";
 563+
 564+ client->cl_entity.he_source.fd.fd = client->cl_cfd;
 565+ client->cl_entity.he_source.fd.size = sb.st_size;
 566+ if (config.compress)
 567+ client->cl_entity.he_encoding = client->cl_enc;
 568+
 569+ client->cl_entity.he_source_type = ENT_SOURCE_FILE;
 570+
 571+ client->cl_flags.f_cached = 1;
 572+ entity_send(client->cl_fde, &client->cl_entity, client_response_done, client, 0);
 573+}
 574+
 575+/*
 576+ * Called when response was finished writing to the client.
 577+ */
 578+/*ARGSUSED*/
 579+static void
 580+client_response_done(http_entity *entity, void *data, int res)
 581+{
 582+struct http_client *client = (http_client *)data;
 583+
 584+ WDEBUG((WLOG_DEBUG, "client_response_done: called, res=%d", res));
 585+
 586+ if (client->cl_cfd) {
 587+ if (close(client->cl_cfd) < 0) {
 588+ wlog(WLOG_WARNING, "closing cache file: %s", strerror(errno));
 589+ }
 590+ }
 591+
 592+ if (client->cl_co) {
 593+ int complete = (res != -1);
 594+ struct header_list *hdr;
 595+
 596+ /*
 597+ * The server may have indicated that we shouldn't cache this document.
 598+ * If so, release it in an incomplete state so it gets evicted.
 599+ */
 600+
 601+ /*
 602+ * HTTP/1.0 Pragma
 603+ */
 604+ hdr = header_find(&client->cl_entity.he_headers, "Pragma");
 605+ if (hdr) {
 606+ char **pragmas = wstrvec(hdr->hl_value, ",", 0);
 607+ char **s;
 608+ for (s = pragmas; *s; ++s) {
 609+ if (!strcasecmp(*s, "no-cache")) {
 610+ complete = 0;
 611+ break;
 612+ }
 613+ }
 614+ wstrvecfree(pragmas);
 615+ }
 616+
 617+ /*
 618+ * HTTP/1.1 Cache-Control
 619+ */
 620+ hdr = header_find(&client->cl_entity.he_headers, "Cache-Control");
 621+ if (hdr) {
 622+ char **controls = wstrvec(hdr->hl_value, ",", 0);
 623+ char **s;
 624+ for (s = controls; *s; ++s) {
 625+ /*
 626+ * According to the standard, we can still cache no-cache
 627+ * documents, but we have to revalidate them on every request.
 628+ */
 629+ if (!strcasecmp(*s, "no-cache") ||
 630+ (!config.cache_private && !strcasecmp(*s, "private")) ||
 631+ !strcasecmp(*s, "no-store")) {
 632+ complete = 0;
 633+ break;
 634+ }
 635+ }
 636+ wstrvecfree(controls);
 637+ }
 638+
 639+ wcache_release(client->cl_co, complete);
 640+ }
 641+
 642+ client_log_request(client);
 643+ client_close(client);
 644+}
 645+
 646+static void
 647+client_close(http_client *client)
 648+{
 649+ WDEBUG((WLOG_DEBUG, "close client %d", client->cl_fde->fde_fd));
 650+ assert(!client->cl_flags.f_closed);
 651+ client->cl_flags.f_closed = 1;
 652+ if (client->cl_wrtbuf)
 653+ wfree(client->cl_wrtbuf);
 654+ if (client->cl_path)
 655+ wfree(client->cl_path);
 656+ entity_free(&client->cl_entity);
 657+ wnet_close(client->cl_fde->fde_fd);
 658+ if (client->cl_backendfde)
 659+ wnet_close(client->cl_backendfde->fde_fd);
 660+
 661+ client->fe_next = freelist.fe_next;
 662+ freelist.fe_next = client;
 663+}
 664+
 665+static void
 666+client_send_error(http_client *client, int errnum, const char * errdata, int status, const char *statstr)
 667+{
 668+ FILE *errfile;
 669+ char errbuf[8192];
 670+ char *p = errbuf, *u;
 671+ ssize_t size;
 672+
 673+ if (client->cl_co)
 674+ wcache_release(client->cl_co, 0);
 675+
 676+ if (errnum >= 0) {
 677+ if ((errfile = fopen(error_files[errnum], "r")) == NULL) {
 678+ client_close(client);
 679+ return;
 680+ }
 681+
 682+ if ((size = fread(errbuf, 1, sizeof(errbuf) - 1, errfile)) < 0) {
 683+ (void)fclose(errfile);
 684+ client_close(client);
 685+ return;
 686+ }
 687+
 688+ (void)fclose(errfile);
 689+ errbuf[size] = '\0';
 690+
 691+ if (!errdata)
 692+ errdata = "Unknown error";
 693+ if (!client->cl_path)
 694+ client->cl_path = wstrdup("NONE");
 695+
 696+ u = NULL;
 697+
 698+ while (*p) {
 699+ switch(*p) {
 700+ case '%':
 701+ switch (*++p) {
 702+ case 'U':
 703+ realloc_strcat(&u, client->cl_path);
 704+ break;
 705+ case 'D':
 706+ realloc_strcat(&u, current_time_str);
 707+ break;
 708+ case 'H':
 709+ realloc_strcat(&u, my_hostname);
 710+ break;
 711+ case 'E':
 712+ realloc_strcat(&u, errdata);
 713+ break;
 714+ case 'V':
 715+ realloc_strcat(&u, my_version);
 716+ break;
 717+ case 'C': {
 718+ char *s = (char *)wmalloc(4);
 719+ sprintf(s, "%d", status);
 720+ realloc_strcat(&u, s);
 721+ wfree(s);
 722+ break;
 723+ }
 724+ case 'S':
 725+ realloc_strcat(&u, statstr);
 726+ break;
 727+ default:
 728+ break;
 729+ }
 730+ p++;
 731+ continue;
 732+ default:
 733+ realloc_addchar(&u, *p);
 734+ break;
 735+ }
 736+ ++p;
 737+ }
 738+
 739+
 740+ client->cl_wrtbuf = u;
 741+ }
 742+
 743+ header_free(&client->cl_entity.he_headers);
 744+ bzero(&client->cl_entity.he_headers, sizeof(client->cl_entity.he_headers));
 745+ header_add(&client->cl_entity.he_headers, wstrdup("Date"), wstrdup(current_time_str));
 746+ header_add(&client->cl_entity.he_headers, wstrdup("Expires"), wstrdup(current_time_str));
 747+ header_add(&client->cl_entity.he_headers, wstrdup("Server"), wstrdup(my_version));
 748+ header_add(&client->cl_entity.he_headers, wstrdup("Connection"), wstrdup("close"));
 749+
 750+ entity_set_response(&client->cl_entity, 1);
 751+ client->cl_entity.he_rdata.response.status = status;
 752+ client->cl_entity.he_rdata.response.status_str = statstr;
 753+ if (errnum >= 0) {
 754+ header_add(&client->cl_entity.he_headers, wstrdup("Content-Type"), wstrdup("text/html"));
 755+ client->cl_entity.he_source_type = ENT_SOURCE_BUFFER;
 756+ client->cl_entity.he_source.buffer.addr = client->cl_wrtbuf;
 757+ client->cl_entity.he_source.buffer.len = strlen(client->cl_wrtbuf);
 758+ } else {
 759+ client->cl_entity.he_source_type = ENT_SOURCE_NONE;
 760+ }
 761+
 762+ client->cl_entity.he_flags.cachable = 0;
 763+ entity_send(client->cl_fde, &client->cl_entity, client_response_done, client, 0);
 764+}
 765+
 766+static void
 767+client_log_request(http_client *client)
 768+{
 769+ int i;
 770+
 771+ if (!config.access_log.size())
 772+ return;
 773+
 774+ i = fprintf(alf, "[%s] %s %s \"%s\" %d %s %s\n",
 775+ current_time_short, client->cl_fde->fde_straddr,
 776+ request_string[client->cl_reqtype],
 777+ client->cl_path, client->cl_entity.he_rdata.response.status,
 778+ client->cl_backend ? client->cl_backend->be_name.c_str() : "-",
 779+ client->cl_flags.f_cached ? "HIT" : "MISS");
 780+ if (i < 0) {
 781+ wlog(WLOG_ERROR, "writing logfile: %s", strerror(errno));
 782+ exit(8);
 783+ }
 784+
 785+ if (fflush(alf) == EOF) {
 786+ wlog(WLOG_ERROR, "flushing logfile: %s", strerror(errno));
 787+ exit(8);
 788+ }
 789+}
 790+
 791+static void
 792+do_cache_write(const char *buf, size_t len, void *data)
 793+{
 794+struct http_client *client = (http_client *)data;
 795+
 796+ if (write(client->cl_cfd, buf, len) < 0) {
 797+ /*EMPTY*/
 798+ WDEBUG((WLOG_WARNING, "writing cached data: %s", strerror(errno)));
 799+ }
 800+ client->cl_co->co_size += len;
 801+}
 802+
 803+static int
 804+removable_header(const char *hdr)
 805+{
 806+static const char *removable_headers[] = {
 807+ "Connection",
 808+ "Keep-Alive",
 809+ "Proxy-Authenticate",
 810+ "Proxy-Authorization",
 811+ "TE",
 812+ "Trailers",
 813+ "Transfer-Encoding",
 814+ "Upgrade",
 815+ NULL,
 816+};
 817+ const char **s;
 818+ for (s = &removable_headers[0]; *s; s++)
 819+ if (!strcasecmp(*s, hdr))
 820+ return 1;
 821+ return 0;
 822+}
 823+
 824+
Index: trunk/willow/src/willow/wlog.cc
@@ -0,0 +1,105 @@
 2+/* @(#) $Header$ */
 3+/* This source code is in the public domain. */
 4+/*
 5+ * Willow: Lightweight HTTP reverse-proxy.
 6+ * wlog: logging.
 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 <stdarg.h>
 15+#include <stdlib.h>
 16+#include <stdarg.h>
 17+#include <string.h>
 18+#include <syslog.h>
 19+#include <errno.h>
 20+
 21+#include "config.h"
 22+
 23+#include "wlog.h"
 24+#include "wnet.h"
 25+#include "wconfig.h"
 26+
 27+struct log_variables logging;
 28+
 29+static const char *sev_names[] = {
 30+ "Debug",
 31+ "Notice",
 32+ "Warning",
 33+ "Error",
 34+};
 35+
 36+static const int syslog_pri[] = {
 37+ LOG_INFO,
 38+ LOG_INFO,
 39+ LOG_WARNING,
 40+ LOG_ERR,
 41+};
 42+
 43+void
 44+wlog_init(void)
 45+{
 46+ if (logging.syslog)
 47+ openlog("willow", LOG_PID, logging.facility);
 48+
 49+ if (logging.file.empty())
 50+ return;
 51+
 52+ /*LINTED unsafe fopen*/
 53+ logging.fp = fopen(logging.file.c_str(), "a");
 54+ if (logging.fp == NULL) {
 55+ wlog(WLOG_ERROR, "Cannot open error log file: %s", logging.file.c_str());
 56+ exit(8);
 57+ }
 58+}
 59+
 60+void
 61+wlog(int sev, const char *fmt, ...)
 62+{
 63+ char s[1024];
 64+ va_list ap;
 65+ int i;
 66+struct timeval tv;
 67+
 68+ if (sev > WLOG_MAX)
 69+ sev = WLOG_NOTICE;
 70+ if (sev < logging.level)
 71+ return;
 72+ va_start(ap, fmt);
 73+ gettimeofday(&tv, NULL);
 74+ i = snprintf(s, 1024, "%s+%.04f| %s: ", current_time_short, tv.tv_usec / 1000000.0, sev_names[sev]);
 75+ if (i > 1023)
 76+ abort();
 77+ if (vsnprintf(s + i, 1023 - i, fmt, ap) > (1023 - i - 1))
 78+ abort();
 79+
 80+ if (logging.syslog)
 81+ syslog(syslog_pri[sev], "%s", s + i);
 82+ if (logging.fp) {
 83+ if (fprintf(logging.fp, "%s\n", s) < 0) {
 84+ (void)fclose(logging.fp);
 85+ logging.fp = NULL;
 86+ wlog(WLOG_ERROR, "writing to logfile: %s", strerror(errno));
 87+ exit(8);
 88+ }
 89+ }
 90+
 91+ if (config.foreground)
 92+ (void)fprintf(stderr, "%s\n", s);
 93+ va_end(ap);
 94+}
 95+
 96+void
 97+wlog_close(void)
 98+{
 99+ if (logging.fp && fclose(logging.fp) == EOF) {
 100+ logging.fp = NULL;
 101+ wlog(WLOG_WARNING, "closing logfile: %s", strerror(errno));
 102+ }
 103+
 104+ if (logging.syslog)
 105+ closelog();
 106+}