Index: trunk/willow/configure.in |
— | — | @@ -1,8 +1,8 @@ |
2 | 2 | # @(#) $Header$ |
3 | 3 | |
4 | | -AC_INIT(willow, 1.0-cvs, keturner@livejournal.com) |
| 4 | +AC_INIT(willow, 1.0-cvs, river@attenuate.org) |
5 | 5 | |
6 | | -USER_CFLAGS="$CFLAGS" |
| 6 | +USER_CXXFLAGS="$CXXFLAGS" |
7 | 7 | |
8 | 8 | # autoconf brokenness |
9 | 9 | if test -z "$CC"; then |
— | — | @@ -10,48 +10,49 @@ |
11 | 11 | fi |
12 | 12 | |
13 | 13 | 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" |
16 | 16 | 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" |
19 | 19 | |
20 | 20 | AC_CONFIG_HEADER(src/include/config.h) |
21 | 21 | |
22 | 22 | echo "" |
23 | 23 | echo "examining environment..." |
24 | | -AC_LANG_C |
| 24 | +AC_LANG(C++) |
25 | 25 | AC_PROG_CC |
| 26 | +AC_PROG_CXX |
26 | 27 | |
27 | 28 | 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 |
30 | 31 | fi |
31 | 32 | |
32 | 33 | AC_MSG_CHECKING(whether we are using the SunPro C compiler) |
33 | 34 | if $CC -V 2>&1 | grep "Sun C" >/dev/null; then |
34 | 35 | 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 |
37 | 38 | else |
38 | 39 | AC_MSG_RESULT(no) |
39 | 40 | fi |
40 | 41 | |
41 | | -mycflags=$prod_cflags |
| 42 | +mycxxflags=$prod_cxxflags |
42 | 43 | AC_ARG_ENABLE(debug, AC_HELP_STRING([--enable-debug],[Compile with debug support]), |
43 | 44 | [ if test $enableval = yes; then |
44 | 45 | AC_DEFINE([WILLOW_DEBUG], [], [Compile with debug support]) |
45 | | - mycflags=$debug_cflags |
| 46 | + mycxxflags=$debug_cxxflags |
46 | 47 | fi |
47 | 48 | ]) |
48 | 49 | |
49 | | -if test -z "$USER_CFLAGS"; then |
50 | | - USE_CFLAGS=$mycflags |
| 50 | +if test -z "$USER_CXXFLAGS"; then |
| 51 | + USE_CXXFLAGS=$mycxxflags |
51 | 52 | else |
52 | | - USE_CFLAGS=$USER_CFLAGS |
| 53 | + USE_CXXFLAGS=$USER_CXXFLAGS |
53 | 54 | fi |
54 | 55 | |
55 | | -echo "using CFLAGS: $USE_CFLAGS" |
| 56 | +echo "using CXXFLAGS: $USE_CXXFLAGS" |
56 | 57 | |
57 | 58 | AC_PROG_INSTALL |
58 | 59 | AC_MAKE_INCLUDE |
— | — | @@ -128,7 +129,7 @@ |
129 | 130 | AC_CHECK_LIB(event, event_add, [],[AC_ERROR([libevent not found])]) |
130 | 131 | |
131 | 132 | datadir="${datadir}/willow" |
132 | | -CFLAGS="$USE_CFLAGS" |
| 133 | +CXXFLAGS="$USE_CXXFLAGS" |
133 | 134 | |
134 | 135 | |
135 | 136 | PATH_LIBEXECDIR=`eval echo $libexecdir | sed -e "s,NONE,$prefix,"` |
Index: trunk/willow/mk/prog.mk.in |
— | — | @@ -10,15 +10,15 @@ |
11 | 11 | _PROGRAM=$(PROGRAM) |
12 | 12 | all: $(_PROGRAM) |
13 | 13 | |
14 | | -OBJS=$(SRCS:.c=.o) $(OBJADD) |
| 14 | +_ALLOBJS=$(OBJS) $(OBJADD) |
15 | 15 | |
16 | | -$(_PROGRAM): $(OBJS) |
| 16 | +$(_PROGRAM): $(_ALLOBJS) |
17 | 17 | @echo " $(_LINK) -o $@" |
18 | 18 | @$(_LINK) -o $@ |
19 | 19 | |
20 | 20 | 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 |
23 | 23 | |
24 | 24 | install: |
25 | 25 | @echo " $(_MKDIR) $(BINDIR)" |
Index: trunk/willow/mk/rules.mk.in |
— | — | @@ -8,6 +8,10 @@ |
9 | 9 | @echo " $(_COMPILE) -c $<" |
10 | 10 | @$(_COMPILE) -c $< |
11 | 11 | |
| 12 | +.cc.o: |
| 13 | + @echo " $(_COMPILEXX) -c $<" |
| 14 | + @$(_COMPILEXX) -c $< |
| 15 | + |
12 | 16 | _dist: |
13 | 17 | [ -d $(DISTDEST)/$(_DISTPATH) ] || mkdir -p $(DISTDEST)/$(_DISTPATH) |
14 | 18 | cp $(_MYDISTFILES) $(DISTDEST)/$(_DISTPATH) |
Index: trunk/willow/mk/vars.mk.in |
— | — | @@ -12,14 +12,17 @@ |
13 | 13 | _YACC= @YACC@ |
14 | 14 | |
15 | 15 | CC= @CC@ |
| 16 | +CXX= @CXX@ |
16 | 17 | _CPPFLAGS= @CPPFLAGS@ |
17 | 18 | _CFLAGS= @CFLAGS@ |
| 19 | +_CXXFLAGS= @CXXFLAGS@ |
18 | 20 | _LDFLAGS= @LDFLAGS@ @LIBS@ |
19 | 21 | |
20 | 22 | SRCROOT= @abs_top_srcdir@ |
21 | 23 | |
22 | | -_LINK= $(CC) $(_CFLAGS) $(CFLAGS) $(_LDFLAGS) $(OBJS) $(LDFLAGS) |
| 24 | +_LINK= $(CXX) $(_CFLAGS) $(CFLAGS) $(_LDFLAGS) $(_ALLOBJS) $(LDFLAGS) |
23 | 25 | _COMPILE= $(CC) $(_CPPFLAGS) $(CPPFLAGS) $(_CFLAGS) $(CFLAGS) |
| 26 | +_COMPILEXX= $(CXX) $(_CPPFLAGS) $(CPPFLAGS) $(_CXXFLAGS) $(CXXFLAGS) |
24 | 27 | _RMF= rm -f |
25 | 28 | _AR= ar |
26 | 29 | _RANLIB= ranlib |
Index: trunk/willow/src/include/wcache.h |
— | — | @@ -36,7 +36,7 @@ |
37 | 37 | time_t co_time; /* Last-Modified / retrieval time */ |
38 | 38 | time_t co_lru; /* Last used timestamp, for eviction */ |
39 | 39 | int co_id; /* Object id */ |
40 | | - int co_plen; /* Size of cache object path */ |
| 40 | + size_t co_plen; /* Size of cache object path */ |
41 | 41 | size_t co_size; /* Object size */ |
42 | 42 | char *co_path; /* Object data location */ |
43 | 43 | int co_complete; /* Finished being cached */ |
Index: trunk/willow/src/include/wconfig.h |
— | — | @@ -15,14 +15,18 @@ |
16 | 16 | #include <sys/types.h> |
17 | 17 | #include <netinet/in.h> |
18 | 18 | |
| 19 | +#include <string> |
| 20 | +using std::string; |
| 21 | +#include <vector> |
| 22 | +using std::vector; |
| 23 | + |
19 | 24 | struct listener { |
20 | | - char *name; |
21 | | - char *host; |
| 25 | + string name; |
| 26 | + string host; |
22 | 27 | int port; |
23 | | -struct sockaddr_in addr; |
| 28 | +struct sockaddr_in addr; |
24 | 29 | }; |
25 | | -extern int nlisteners; |
26 | | -extern struct listener **listeners; |
| 30 | +extern vector<listener *> listeners; |
27 | 31 | |
28 | 32 | struct cachedir { |
29 | 33 | char *dir; |
— | — | @@ -31,22 +35,22 @@ |
32 | 36 | |
33 | 37 | extern struct configuration { |
34 | 38 | int foreground; |
35 | | -const char *access_log; |
| 39 | + string access_log; |
36 | 40 | struct cachedir *caches; |
37 | 41 | int ncaches; |
38 | 42 | time_t cache_expevery; |
39 | 43 | int cache_expthresh; |
40 | | - char *suid, *sgid; |
41 | | - int compress; |
| 44 | + string suid, sgid; |
| 45 | + bool compress; |
42 | 46 | 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; |
46 | 50 | } config; |
47 | 51 | |
48 | | -void wconfig_init(const char *); |
| 52 | +void wconfig_init(char const *); |
49 | 53 | |
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); |
52 | 56 | |
53 | 57 | #endif |
Index: trunk/willow/src/include/willow.h |
— | — | @@ -14,8 +14,20 @@ |
15 | 15 | |
16 | 16 | #include "config.h" |
17 | 17 | |
18 | | -typedef long long w_size_t; |
| 18 | +#include <sstream> |
19 | 19 | |
| 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 | + |
20 | 32 | #ifdef WDEBUG_ALLOC |
21 | 33 | void *internal_wmalloc(size_t, const char *, int); |
22 | 34 | void internal_wfree(void *, const char *, int); |
— | — | @@ -52,10 +64,10 @@ |
53 | 65 | #endif |
54 | 66 | |
55 | 67 | #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); |
57 | 69 | #endif |
58 | 70 | #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); |
60 | 72 | #endif |
61 | 73 | |
62 | 74 | void outofmemory(void); |
— | — | @@ -63,8 +75,6 @@ |
64 | 76 | # pragma does_not_return(outofmemory) |
65 | 77 | #endif |
66 | 78 | |
67 | | -#define min(x,y) ((x) < (y) ? (x) : (y)) |
68 | | - |
69 | 79 | #define CHAR_HOST 1 |
70 | 80 | |
71 | 81 | extern int char_table[]; |
Index: trunk/willow/src/include/wbackend.h |
— | — | @@ -13,16 +13,21 @@ |
14 | 14 | #endif |
15 | 15 | |
16 | 16 | #include <sys/types.h> |
17 | | - |
18 | 17 | #include <netinet/in.h> |
19 | 18 | |
| 19 | +#include <string> |
| 20 | +using std::string; |
| 21 | +#include <vector> |
| 22 | +using std::vector; |
| 23 | + |
20 | 24 | struct fde; |
21 | 25 | |
22 | 26 | 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 */ |
24 | 29 | int be_port; /* port number */ |
25 | 30 | struct sockaddr_in be_addr; /* socket address */ |
26 | | - char *be_straddr; /* formatted address */ |
| 31 | + string be_straddr; /* formatted address */ |
27 | 32 | int be_dead; /* 0 if okay, 1 if unavailable */ |
28 | 33 | time_t be_time; /* If dead, time to retry */ |
29 | 34 | uint32_t be_hash; /* constant carp "host" hash */ |
— | — | @@ -33,11 +38,11 @@ |
34 | 39 | |
35 | 40 | typedef void (*backend_cb)(struct backend *, struct fde *, void *); |
36 | 41 | |
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 &); |
39 | 44 | |
40 | | -int get_backend(const char *url, backend_cb, void *, int); |
| 45 | +int get_backend(string const &url, backend_cb, void *, int); |
41 | 46 | |
42 | | -extern int nbackends; |
| 47 | +extern vector<backend *> backends; |
43 | 48 | |
44 | 49 | #endif |
Index: trunk/willow/src/include/confparse.h |
— | — | @@ -1,6 +1,7 @@ |
2 | 2 | /* @(#) $Header$ */ |
3 | 3 | /* From: $Nightmare: nightmare/include/config.h,v 1.32.2.2.2.2 2002/07/02 03:41:28 ejb Exp $ */ |
4 | 4 | /* 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 */ |
5 | 6 | /* This source code is in the public domain. */ |
6 | 7 | /* |
7 | 8 | * Willow: Lightweight HTTP reverse-proxy. |
— | — | @@ -14,65 +15,382 @@ |
15 | 16 | # pragma ident "@(#)$Header$" |
16 | 17 | #endif |
17 | 18 | |
18 | | -#include "queue.h" |
| 19 | +#include <string> |
| 20 | +#include <map> |
| 21 | +#include <vector> |
| 22 | +#include <assert.h> |
19 | 23 | |
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; |
27 | 74 | }; |
28 | 75 | |
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 |
36 | 82 | }; |
37 | 83 | |
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(); |
45 | 86 | |
46 | | -#define CF_MTYPE 0xFF |
| 87 | + string av_strval; |
| 88 | + long av_intval; |
| 89 | + int av_type; |
| 90 | +}; |
47 | 91 | |
48 | | -#define CF_FLIST 0x1000 |
49 | | -#define CF_MFLAG 0xFF00 |
| 92 | +struct value { |
| 93 | + value(declpos const &pos); |
50 | 94 | |
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; |
61 | 99 | |
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 | +}; |
63 | 105 | |
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 &); |
67 | 112 | |
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 | +}; |
77 | 121 | |
| 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); |
78 | 148 | |
| 149 | + vector<tree_entry> entries; |
| 150 | +}; |
| 151 | + |
| 152 | +extern tree global_conf_tree; |
| 153 | +#ifdef NEED_PARSING_TREE |
| 154 | +extern tree parsing_tree; |
79 | 155 | #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 @@ |
13 | 13 | # pragma ident "@(#)$Header$" |
14 | 14 | #endif |
15 | 15 | |
16 | | -#include <stdio.h> |
| 16 | +#include <cstdio> |
| 17 | +#include <string> |
| 18 | +using std::string; |
17 | 19 | |
18 | 20 | #include "config.h" |
19 | 21 | |
— | — | @@ -23,11 +25,11 @@ |
24 | 26 | #define WLOG_MAX 3 |
25 | 27 | |
26 | 28 | 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; |
32 | 34 | } logging; |
33 | 35 | |
34 | 36 | 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(¤t_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$ |
9 | 5 | */ |
10 | 6 | |
11 | 7 | %{ |
12 | | -#if defined __SUNPRO_CC || defined __DECC || defined __HP_cc |
13 | | -# pragma ident "@(#)$Header$" |
14 | | -#endif |
15 | | - |
16 | 8 | #include <sys/types.h> |
17 | 9 | #include <sys/stat.h> |
18 | 10 | |
19 | 11 | #include <netinet/in.h> |
| 12 | +#include <arpa/inet.h> |
| 13 | +#include <netdb.h> |
20 | 14 | |
21 | | -#include <string.h> |
22 | | -#include <stdlib.h> |
23 | | -#include <stdarg.h> |
24 | | -#include <stdio.h> |
| 15 | +#include <vector> |
| 16 | +using std::vector; |
25 | 17 | |
| 18 | +#include <cstdlib> |
| 19 | +#include <cstdarg> |
| 20 | +#include <cstdio> |
| 21 | + |
| 22 | +#define NEED_PARSING_TREE |
26 | 23 | #include "willow.h" |
27 | 24 | #include "confparse.h" |
28 | 25 | |
29 | 26 | #define YY_NO_UNPUT |
30 | 27 | |
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 |
34 | 32 | |
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 &); |
37 | 34 | |
38 | 35 | static struct { |
39 | 36 | const char * name; |
— | — | @@ -59,24 +56,130 @@ |
60 | 57 | {NULL, NULL, 0}, |
61 | 58 | }; |
62 | 59 | |
63 | | -static time_t |
64 | | -conf_find_time(name) |
65 | | - const char *name; |
| 60 | +time_t conf_find_time(string const &name) |
66 | 61 | { |
67 | | - int i; |
| 62 | + int i; |
68 | 63 | |
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; |
73 | 110 | } |
| 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 | +} |
74 | 125 | |
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; |
76 | 163 | } |
77 | 164 | |
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; |
81 | 184 | } yesno[] = { |
82 | 185 | {"yes", 1}, |
83 | 186 | {"no", 0}, |
— | — | @@ -88,230 +191,349 @@ |
89 | 192 | }; |
90 | 193 | |
91 | 194 | static int |
92 | | -conf_get_yesno_value(str) |
93 | | - const char *str; |
| 195 | +conf_get_yesno_value(string const &str) |
94 | 196 | { |
95 | | - int i; |
96 | | - |
| 197 | +int i; |
97 | 198 | for (i = 0; yesno[i].word; i++) |
98 | | - if (!strcasecmp(str, yesno[i].word)) |
| 199 | + if (str == yesno[i].word) |
99 | 200 | return yesno[i].yesno; |
100 | 201 | |
101 | 202 | return -1; |
102 | 203 | } |
103 | 204 | |
104 | | -static void |
105 | | -free_cur_list(list) |
106 | | - conf_parm_t *list; |
107 | | -{ |
108 | | - switch (list->type & CF_MTYPE) { |
109 | | - case CF_STRING: |
110 | | - case CF_QSTRING: |
111 | | - wfree(list->v.string); |
112 | | - break; |
113 | | - case CF_LIST: |
114 | | - free_cur_list(list->v.list); |
115 | | - break; |
116 | | - default: break; |
117 | | - } |
118 | | - |
119 | | - if (list->next) |
120 | | - free_cur_list(list->next); |
121 | | -} |
122 | | - |
123 | | - |
124 | | -conf_parm_t * cur_list = NULL; |
125 | | - |
126 | | -static void |
127 | | -add_cur_list_cpt(new) |
128 | | - conf_parm_t *new; |
129 | | -{ |
130 | | - if (cur_list == NULL) { |
131 | | - cur_list = wcalloc(1, sizeof(conf_parm_t)); |
132 | | - cur_list->type |= CF_FLIST; |
133 | | - cur_list->v.list = new; |
134 | | - } else { |
135 | | - new->next = cur_list->v.list; |
136 | | - cur_list->v.list = new; |
137 | | - } |
138 | | -} |
139 | | - |
140 | | -static void |
141 | | -add_cur_list(type, str, number) |
142 | | - int type, number; |
143 | | - const char *str; |
144 | | -{ |
145 | | - conf_parm_t *new; |
146 | | - |
147 | | - new = wmalloc(sizeof(conf_parm_t)); |
148 | | - new->next = NULL; |
149 | | - new->type = type; |
150 | | - |
151 | | - switch(type) { |
152 | | - case CF_INT: |
153 | | - case CF_TIME: |
154 | | - case CF_YESNO: |
155 | | - new->v.number = number; |
156 | | - break; |
157 | | - case CF_STRING: |
158 | | - case CF_QSTRING: |
159 | | - new->v.string = wstrdup(str); |
160 | | - break; |
161 | | - } |
162 | | - |
163 | | - add_cur_list_cpt(new); |
164 | | -} |
165 | | - |
166 | | - |
167 | 205 | %} |
168 | 206 | |
169 | 207 | %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_; |
173 | 217 | } |
174 | 218 | |
175 | | -%token TWODOTS |
| 219 | +%token TWODOTS VAR TEMPLATE FROM |
176 | 220 | |
177 | | -%token <string> QSTRING STRING |
| 221 | +%token <string_> QSTRING STRING VARNAME |
178 | 222 | %token <number> NUMBER |
179 | 223 | |
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 |
183 | 234 | |
| 235 | +%left '+' |
| 236 | +%nonassoc poneitem |
| 237 | +%nonassoc ptimespec |
184 | 238 | %start conf |
185 | 239 | |
186 | 240 | %% |
187 | 241 | |
188 | 242 | conf: |
189 | 243 | | conf conf_item |
190 | | - | error |
191 | 244 | ; |
192 | 245 | |
193 | 246 | conf_item: block |
194 | 247 | ; |
195 | 248 | |
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 | + ; |
218 | 261 | |
219 | | -block_items: block_items block_item |
220 | | - | block_item |
221 | | - ; |
| 262 | +template_clause: |
| 263 | + { $$ = 0; } |
| 264 | + | TEMPLATE { $$ = 1; } |
| 265 | + ; |
222 | 266 | |
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); |
229 | 294 | } |
230 | | - ; |
| 295 | + free($1); |
| 296 | + } |
| 297 | + ; |
231 | 298 | |
| 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 | + */ |
232 | 389 | itemlist: itemlist ',' single |
| 390 | + { |
| 391 | + $$->insert($$->end(), $3->begin(), $3->end()); |
| 392 | + delete $3; |
| 393 | + delete $1; |
| 394 | + } |
233 | 395 | | single |
| 396 | + { |
| 397 | + $$ = new vector<conf::avalue>; |
| 398 | + $$->insert($$->end(), $1->begin(), $1->end()); |
| 399 | + delete $1; |
| 400 | + } |
234 | 401 | ; |
235 | 402 | |
236 | 403 | single: oneitem |
237 | 404 | { |
238 | | - add_cur_list_cpt($1); |
| 405 | + $$ = new vector<conf::avalue>; |
| 406 | + $$->push_back(*$1); |
| 407 | + delete $1; |
239 | 408 | } |
240 | 409 | | oneitem TWODOTS oneitem |
241 | 410 | { |
| 411 | + $$ = new vector<conf::avalue>; |
242 | 412 | /* "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."); |
246 | 415 | break; |
247 | 416 | } 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); |
252 | 423 | } |
253 | 424 | } |
| 425 | + delete $1; |
| 426 | + delete $3; |
254 | 427 | } |
| 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 | + } |
255 | 455 | ; |
256 | 456 | |
257 | | -oneitem: qstring |
| 457 | +oneitem: astring |
258 | 458 | { |
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; |
263 | 463 | } |
264 | | - | timespec |
| 464 | + | timespec |
265 | 465 | { |
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; |
269 | 469 | } |
270 | 470 | | number |
271 | 471 | { |
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; |
275 | 475 | } |
276 | 476 | | string |
277 | 477 | { |
278 | 478 | /* a 'string' could also be a yes/no value .. |
279 | 479 | so pass it as that, if so */ |
280 | | - int val = conf_get_yesno_value($1); |
| 480 | + int val = conf_get_yesno_value(*$1); |
281 | 481 | |
282 | | - $$ = wmalloc(sizeof(conf_parm_t)); |
| 482 | + $$ = new conf::avalue; |
283 | 483 | |
284 | 484 | if (val != -1) { |
285 | | - $$->type = CF_YESNO; |
286 | | - $$->v.number = val; |
| 485 | + $$->av_type = conf::cv_yesno; |
| 486 | + $$->av_intval = val; |
287 | 487 | } else { |
288 | | - $$->type = CF_STRING; |
289 | | - $$->v.string = wstrdup($1); |
| 488 | + $$->av_type = conf::cv_string; |
| 489 | + $$->av_strval = *$1; |
290 | 490 | } |
291 | | - wfree($1); |
| 491 | + delete $1; |
292 | 492 | } |
293 | 493 | ; |
294 | 494 | |
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; } ; |
297 | 523 | number: NUMBER { $$ = $1; } ; |
| 524 | +varname: VARNAME { $$ = $1; } ; |
298 | 525 | |
299 | | -timespec: number string |
| 526 | +timespec: number string |
300 | 527 | { |
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()); |
305 | 531 | t = 1; |
306 | 532 | } |
307 | | - |
| 533 | + delete $2; |
308 | 534 | $$ = $1 * t; |
309 | 535 | } |
310 | 536 | | timespec timespec |
311 | 537 | { |
312 | 538 | $$ = $1 + $2; |
313 | 539 | } |
314 | | - | timespec number |
315 | | - { |
316 | | - $$ = $1 + $2; |
317 | | - } |
318 | 540 | ; |
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. */ |
5 | 2 | /* |
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> |
8 | 6 | */ |
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 $ */ |
13 | 8 | |
14 | | -#include <string.h> |
15 | | -#include <errno.h> |
| 9 | +%option noyywrap |
| 10 | +%option nounput |
16 | 11 | |
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 | + |
18 | 22 | #include "confparse.h" |
19 | 23 | #include "y.tab.h" |
20 | 24 | |
21 | | -int lineno = 1; |
| 25 | +struct func_attrs; |
22 | 26 | |
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 | + |
28 | 42 | %} |
29 | 43 | |
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}$ |
31 | 54 | |
32 | | -ws [ \t]* |
33 | | -number [0-9][0-9]* |
34 | | -comment #.* |
35 | | -qstring \"[^\"\n]*[\"] |
36 | | -string [a-zA-Z_\~][a-zA-Z0-9_-]* |
37 | 55 | |
| 56 | +%x COMMENT IGNORING |
38 | 57 | %% |
39 | 58 | |
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++; } |
42 | 69 | |
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 | + } |
56 | 106 | } |
57 | | - return NUMBER; |
58 | 107 | } |
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(); |
61 | 117 | } |
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; |
64 | 129 | return QSTRING; |
65 | 130 | } |
66 | | -. { return yytext[0]; |
| 131 | +{string} { |
| 132 | + yylval.string_ = new std::string(yytext, yyleng); |
| 133 | + oldpos = conf::curpos; conf::curpos += yyleng; |
| 134 | + return STRING; |
67 | 135 | } |
| 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 @@ |
3 | 3 | BINDIR = @bindir@ |
4 | 4 | CPPFLAGS = -I../include |
5 | 5 | |
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 |
7 | 18 | SRCS=$(BASESRCS) |
8 | 19 | OBJADD = @LIBOBJS@ y.tab.o lex.yy.o |
9 | 20 | |
— | — | @@ -11,20 +22,22 @@ |
12 | 23 | daemon.c \ |
13 | 24 | Makefile.in |
14 | 25 | |
15 | | -LDFLAGS = -L$(SRCROOT)/src/lib/wnet -lwillownet \ |
16 | | - -L$(SRCROOT)/src/lib/wlog -lwillowlog \ |
17 | | - $(LIBOBJS) |
| 26 | +LDFLAGS = $(LIBOBJS) |
18 | 27 | |
| 28 | +.SUFFIXES: .y .l .c .cc .o |
| 29 | + |
19 | 30 | default: all |
20 | 31 | |
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 |
23 | 34 | |
24 | | -y.tab.c y.tab.h: parser.y |
| 35 | +y.tab.cc y.tab.h: parser.y |
25 | 36 | @echo " $(_YACC) -d parser.y" |
26 | 37 | @$(_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 |
28 | 40 | @echo " $(_LEX) lexer.l" |
29 | 41 | @$(_LEX) lexer.l || rm -f lex.yy.c |
| 42 | + @test -f lex.yy.c && mv lex.yy.c lex.yy.cc |
30 | 43 | |
31 | 44 | @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 | +} |