diff --git a/CHANGELOG b/CHANGELOG index 8057f87c0..df96dd6d8 100644 --- a/CHANGELOG +++ b/CHANGELOG @@ -1,6 +1,12 @@ ChangeLog : =========== +2005/01/22 : 1.2.4 + - merged Alexander Lazic's and Klaus Wagner's work on application + cookie-based persistence. Since this is the first merge, this version is + not intended for general use and reports are more than welcome. Some + documentation is really needed though. + 2005/01/22 : 1.2.3 (1.1.30) - add an architecture guide to the documentation - released without any changes diff --git a/Makefile b/Makefile index 7caf492c4..29a79fcb6 100644 --- a/Makefile +++ b/Makefile @@ -73,7 +73,7 @@ TARGET_OPTS=$(COPTS.$(TARGET)) REGEX_OPTS=$(COPTS.$(REGEX)) CPU_OPTS=$(COPTS.$(CPU)) -COPTS=$(CPU_OPTS) $(TARGET_OPTS) $(REGEX_OPTS) $(SMALL_OPTS) +COPTS=-I. $(CPU_OPTS) $(TARGET_OPTS) $(REGEX_OPTS) $(SMALL_OPTS) LIBS=$(LIBS.$(TARGET)) $(LIBS.$(REGEX)) # - use -DSTATTIME=0 to disable statistics, else specify an interval in @@ -84,12 +84,12 @@ LDFLAGS = -g all: haproxy -haproxy: haproxy.o +haproxy: src/list.o src/chtbl.o src/hashpjw.o haproxy.o $(LD) $(LDFLAGS) -o $@ $^ $(LIBS) %.o: %.c $(CC) $(CFLAGS) -c -o $@ $< clean: - rm -f *.[oas] *~ *.rej core haproxy test nohup.out gmon.out + rm -f *.[oas] *~ *.rej core haproxy test nohup.out gmon.out src/*.[oas] diff --git a/TODO b/TODO index 46e291288..047468b3b 100644 --- a/TODO +++ b/TODO @@ -136,7 +136,7 @@ Todo for 1.2 * listen [ip4.ip4.ip4.ip4]:port[-port] * listen [ip6::...ip6]/port[-port] - server xxx ipv4 | ipv4: | ipv4:port[-port] | ipv6/ | ipv6/port[-port] -- appcookie +* appcookie - weighted round robin - option to shutdown(listen_sock) when max connections reached diff --git a/haproxy.c b/haproxy.c index b6682b43b..87c414574 100644 --- a/haproxy.c +++ b/haproxy.c @@ -57,7 +57,51 @@ #include #endif -#define HAPROXY_VERSION "1.2.3" +#if defined(__dietlibc__) +#include +#endif + +#define TBLSIZ 10 +#define TBLCHKINT 5000 /* The time between two calls of appsession_refresh in ms */ + +/* + These Parts are copied from + + http://www.oreilly.com/catalog/masteralgoc/index.html + Mastering Algorithms with C + By Kyle Loudon + ISBN: 1-56592-453-3 + Publishd by O'Reilly + + We have added our own struct to these function. + */ + +#include +#include +#include +/* end of copied parts */ + +struct app_pool { + void **sessid; + void **serverid; + int ses_waste, ses_use, ses_msize; + int ser_waste, ser_use, ser_msize; +}; + +struct app_pool apools; +int have_appsession; + +/* Callback for hash_lookup */ +int match_str(const void *key1, const void *key2); + +/* Callback for destroy */ +void destroy(void *data); + +#if defined(DEBUG_HASH) +static void print_table(const CHTbl *htbl); +#endif + +#define HAPROXY_VERSION "1.2.4" #define HAPROXY_DATE "2005/01/22" /* this is for libc5 for example */ @@ -236,8 +280,10 @@ int strlcpy2(char *dst, const char *src, int size) { #define sizeof_session sizeof(struct session) #define sizeof_buffer sizeof(struct buffer) #define sizeof_fdtab sizeof(struct fdtab) +#define sizeof_curappsession CAPTURE_LEN /* current_session pool */ #define sizeof_requri REQURI_LEN #define sizeof_capture CAPTURE_LEN +#define sizeof_appsess sizeof(struct appsessions) /* different possible states for the sockets */ #define FD_STCLOSE 0 @@ -501,7 +547,12 @@ struct proxy { struct server *srv, *cursrv; /* known servers, current server */ int nbservers; /* # of servers */ char *cookie_name; /* name of the cookie to look for */ - int cookie_len; /* strlen(cookie_len), computed only once */ + int cookie_len; /* strlen(cookie_name), computed only once */ + char *appsession_name; /* name of the cookie to look for */ + int appsession_name_len; /* strlen(appsession_name), computed only once */ + int appsession_len; /* length of the appsession cookie value to be used */ + int appsession_timeout; + CHTbl htbl_proxy; /* Per Proxy hashtable */ char *capture_name; /* beginning of the name of the cookie to capture */ int capture_namelen; /* length of the cookie name to match */ int capture_len; /* length of the string to be captured */ @@ -598,7 +649,8 @@ void **pool_session = NULL, **pool_fdtab = NULL, **pool_requri = NULL, **pool_task = NULL, - **pool_capture = NULL; + **pool_capture = NULL, + **pool_appsess = NULL; struct proxy *proxy = NULL; /* list of all existing proxies */ struct fdtab *fdtab = NULL; /* array of all the file descriptors */ @@ -745,6 +797,10 @@ int event_srv_read(int fd); int event_srv_write(int fd); int process_session(struct task *t); +static int appsession_task_init(void); +static int appsession_init(void); +static int appsession_refresh(struct task *t); + /*********************************************************************/ /* general purpose functions ***************************************/ /*********************************************************************/ @@ -1605,7 +1661,9 @@ static inline struct server *find_server(struct proxy *px) { int connect_server(struct session *s) { int fd; - // fprintf(stderr,"connect_server : s=%p\n",s); +#ifdef DEBUG_FULL + fprintf(stderr,"connect_server : s=%p\n",s); +#endif if (s->flags & SN_DIRECT) { /* srv cannot be null */ s->srv_addr = s->srv->addr; @@ -1729,7 +1787,9 @@ int event_cli_read(int fd) { struct buffer *b = s->req; int ret, max; - // fprintf(stderr,"event_cli_read : fd=%d, s=%p\n", fd, s); +#ifdef DEBUG_FULL + fprintf(stderr,"event_cli_read : fd=%d, s=%p\n", fd, s); +#endif if (fdtab[fd].state != FD_STERROR) { while (1) { @@ -1824,7 +1884,9 @@ int event_srv_read(int fd) { struct buffer *b = s->rep; int ret, max; - // fprintf(stderr,"event_srv_read : fd=%d, s=%p\n", fd, s); +#ifdef DEBUG_FULL + fprintf(stderr,"event_srv_read : fd=%d, s=%p\n", fd, s); +#endif if (fdtab[fd].state != FD_STERROR) { while (1) { @@ -1918,7 +1980,9 @@ int event_cli_write(int fd) { struct buffer *b = s->rep; int ret, max; - // fprintf(stderr,"event_cli_write : fd=%d, s=%p\n", fd, s); +#ifdef DEBUG_FULL + fprintf(stderr,"event_cli_write : fd=%d, s=%p\n", fd, s); +#endif if (b->l == 0) { /* let's realign the buffer to optimize I/O */ b->r = b->w = b->h = b->lr = b->data; @@ -2005,7 +2069,9 @@ int event_srv_write(int fd) { struct buffer *b = s->req; int ret, max; - //fprintf(stderr,"event_srv_write : fd=%d, s=%p\n", fd, s); +#ifdef DEBUG_FULL + fprintf(stderr,"event_srv_write : fd=%d, s=%p\n", fd, s); +#endif if (b->l == 0) { /* let's realign the buffer to optimize I/O */ b->r = b->w = b->h = b->lr = b->data; @@ -2694,6 +2760,9 @@ int process_cli(struct session *t) { int c = t->cli_state; struct buffer *req = t->req; struct buffer *rep = t->rep; + int method_checked = 0; + appsess *asession_temp = NULL; + appsess local_asession; #ifdef DEBUG_FULL fprintf(stderr,"process_cli: c=%s, s=%s\n", cli_stnames[c], srv_stnames[s]); @@ -2707,6 +2776,7 @@ int process_cli(struct session *t) { while (req->lr < req->r) { /* this loop only sees one header at each iteration */ char *ptr; int delete_header; + char *request_line = NULL; ptr = req->lr; @@ -2815,6 +2885,88 @@ int process_cli(struct session *t) { * req->r = end of data (not used at this stage) */ + if (!method_checked && (t->proxy->appsession_name != NULL) && + ((memcmp(req->h, "GET ", 4) == 0) || (memcmp(req->h, "POST ", 4) == 0)) && + ((request_line = memchr(req->h, ';', req->lr - req->h)) != NULL)) { + + /* skip ; */ + request_line++; + + /* look if we have a jsessionid */ + + if (strncasecmp(request_line, t->proxy->appsession_name, t->proxy->appsession_name_len) == 0) { + + /* skip jsessionid= */ + request_line += t->proxy->appsession_name_len + 1; + + /* First try if we allready have an appsession */ + asession_temp = &local_asession; + + if ((asession_temp->sessid = pool_alloc_from(apools.sessid, apools.ses_msize)) == NULL) { + Alert("Not enough memory process_cli():asession_temp->sessid:calloc().\n"); + send_log(t->proxy, LOG_ALERT, "Not enough Memory process_cli():asession_temp->sessid:calloc().\n"); + return 0; + } + + /* Copy the sessionid */ + memcpy(asession_temp->sessid, request_line, t->proxy->appsession_len); + asession_temp->sessid[t->proxy->appsession_len] = 0; + asession_temp->serverid = NULL; + + /* only do insert, if lookup fails */ + if (chtbl_lookup(&(t->proxy->htbl_proxy), (void *)&asession_temp)) { + if ((asession_temp = pool_alloc(appsess)) == NULL) { + Alert("Not enough memory process_cli():asession:calloc().\n"); + send_log(t->proxy, LOG_ALERT, "Not enough memory process_cli():asession:calloc().\n"); + return 0; + } + asession_temp->sessid = local_asession.sessid; + asession_temp->serverid = local_asession.serverid; + chtbl_insert(&(t->proxy->htbl_proxy), (void *) asession_temp); + } /* end if(chtbl_lookup()) */ + else{ + /*free wasted memory;*/ + pool_free_to(apools.sessid, local_asession.sessid); + } + + tv_delayfrom(&asession_temp->expire, &now, t->proxy->appsession_timeout); + asession_temp->request_count++; + +#if defined(DEBUG_HASH) + print_table(&(t->proxy->htbl_proxy)); +#endif + + if (asession_temp->serverid == NULL) { + Alert("Found Application Session without matching server.\n"); + } else { + struct server *srv = t->proxy->srv; + while (srv) { + if (strcmp(srv->id, asession_temp->serverid) == 0) { + if (srv->state & SRV_RUNNING || t->proxy->options & PR_O_PERSIST) { + /* we found the server and it's usable */ + t->flags &= ~SN_CK_MASK; + t->flags |= SN_CK_VALID | SN_DIRECT; + t->srv = srv; + break; + }else { + t->flags &= ~SN_CK_MASK; + t->flags |= SN_CK_DOWN; + } + }/* end if(strcmp()) */ + srv = srv->next; + }/* end while(srv) */ + }/* end else of if (asession_temp->serverid == NULL) */ + + method_checked = 1; + }/* end if(strncasecmp(request_line,t->proxy->appsession_name,apssesion_name_len) == 0) */ + else { + //fprintf(stderr,">>>>>>>>>>>>>>>>>>>>>>NO SESSION\n"); + } + }/* end if(!method_checked ...) */ + else{ + //printf("No Methode-Header with Session-String\n"); + } + if (t->logs.logwait & LW_REQ) { /* we have a complete HTTP request that we must log */ int urilen; @@ -2932,7 +3084,8 @@ int process_cli(struct session *t) { * remove it. If no application cookie persists in the header, we * *MUST* delete it */ - if (!delete_header && (t->proxy->cookie_name != NULL || t->proxy->capture_name != NULL) + if (!delete_header && + (t->proxy->cookie_name != NULL || t->proxy->capture_name != NULL || t->proxy->appsession_name !=NULL) && !(t->flags & SN_CLDENY) && (ptr >= req->h + 8) && (strncasecmp(req->h, "Cookie: ", 8) == 0)) { char *p1, *p2, *p3, *p4; @@ -3001,12 +3154,12 @@ int process_cli(struct session *t) { if ((t->logs.cli_cookie = pool_alloc(capture)) == NULL) { Alert("HTTP logging : out of memory.\n"); + } else { + if (log_len > t->proxy->capture_len) + log_len = t->proxy->capture_len; + memcpy(t->logs.cli_cookie, p1, log_len); + t->logs.cli_cookie[log_len] = 0; } - - if (log_len > t->proxy->capture_len) - log_len = t->proxy->capture_len; - memcpy(t->logs.cli_cookie, p1, log_len); - t->logs.cli_cookie[log_len] = 0; } if ((p2 - p1 == t->proxy->cookie_len) && (t->proxy->cookie_name != NULL) && @@ -3051,8 +3204,7 @@ int process_cli(struct session *t) { t->flags |= SN_CK_VALID | SN_DIRECT; t->srv = srv; break; - } - else { + } else { /* we found a server, but it's down */ t->flags &= ~SN_CK_MASK; t->flags |= SN_CK_DOWN; @@ -3086,8 +3238,7 @@ int process_cli(struct session *t) { del_cookie = p1; del_colon = colon; } - } - else { + } else { /* now we know that we must keep this cookie since it's * not ours. But if we wanted to delete our cookie * earlier, we cannot remove the complete header, but we @@ -3102,6 +3253,66 @@ int process_cli(struct session *t) { del_cookie = del_colon = NULL; } } + + if ((t->proxy->appsession_name != NULL) && + (memcmp(p1, t->proxy->appsession_name, p2 - p1) == 0)) { + /* first, let's see if the cookie is our appcookie*/ + + /* Cool... it's the right one */ + + asession_temp = &local_asession; + + if ((asession_temp->sessid = pool_alloc_from(apools.sessid, apools.ses_msize)) == NULL) { + Alert("Not enough memory process_cli():asession->sessid:malloc().\n"); + send_log(t->proxy, LOG_ALERT, "Not enough memory process_cli():asession->sessid:malloc().\n"); + return 0; + } + + memcpy(asession_temp->sessid, p3, t->proxy->appsession_len); + asession_temp->sessid[t->proxy->appsession_len] = 0; + asession_temp->serverid = NULL; + + /* only do insert, if lookup fails */ + if (chtbl_lookup(&(t->proxy->htbl_proxy), (void *) &asession_temp) != 0) { + if ((asession_temp = pool_alloc(appsess)) == NULL) { + Alert("Not enough memory process_cli():asession:calloc().\n"); + send_log(t->proxy, LOG_ALERT, "Not enough memory process_cli():asession:calloc().\n"); + return 0; + } + + asession_temp->sessid = local_asession.sessid; + asession_temp->serverid = local_asession.serverid; + chtbl_insert(&(t->proxy->htbl_proxy), (void *) asession_temp); + } + else{ + /* free wasted memory */ + pool_free_to(apools.sessid, local_asession.sessid); + } + + if (asession_temp->serverid == NULL) { + Alert("Found Application Session without matching server.\n"); + } else { + struct server *srv = t->proxy->srv; + while (srv) { + if(strcmp(srv->id, asession_temp->serverid) == 0) { + if (srv->state & SRV_RUNNING || t->proxy->options & PR_O_PERSIST) { + /* we found the server and it's usable */ + t->flags &= ~SN_CK_MASK; + t->flags |= SN_CK_VALID | SN_DIRECT; + t->srv = srv; + break; + } else { + t->flags &= ~SN_CK_MASK; + t->flags |= SN_CK_DOWN; + } + } + srv = srv->next; + }/* end while(srv) */ + }/* end else if server == NULL */ + + tv_delayfrom(&asession_temp->expire, &now, t->proxy->appsession_timeout); + break; + }/* end if ((t->proxy->appsession_name != NULL) ... */ } /* we'll have to look for another cookie ... */ @@ -3413,6 +3624,8 @@ int process_srv(struct session *t) { int c = t->cli_state; struct buffer *req = t->req; struct buffer *rep = t->rep; + appsess *asession_temp = NULL; + appsess local_asession; #ifdef DEBUG_FULL fprintf(stderr,"process_srv: c=%s, s=%s\n", cli_stnames[c], srv_stnames[s]); @@ -3829,7 +4042,7 @@ int process_srv(struct session *t) { /* check for server cookies */ if (!delete_header /*&& (t->proxy->options & PR_O_COOK_ANY)*/ - && (t->proxy->cookie_name != NULL || t->proxy->capture_name != NULL) + && (t->proxy->cookie_name != NULL || t->proxy->capture_name != NULL || t->proxy->appsession_name !=NULL) && (strncasecmp(rep->h, "Set-Cookie: ", 12) == 0)) { char *p1, *p2, *p3, *p4; @@ -3915,6 +4128,58 @@ int process_srv(struct session *t) { } break; } + + /* first, let's see if the cookie is our appcookie*/ + if ((t->proxy->appsession_name != NULL) && + (memcmp(p1, t->proxy->appsession_name, p2 - p1) == 0)) { + + /* Cool... it's the right one */ + + size_t server_id_len = strlen(t->srv->id)+1; + asession_temp = &local_asession; + + if((asession_temp->sessid = pool_alloc_from(apools.sessid, apools.ses_msize)) == NULL){ + Alert("Not enought Memory process_srv():asession->sessid:malloc().\n"); + send_log(t->proxy, LOG_ALERT, "Not enought Memory process_srv():asession->sessid:malloc().\n"); + } + memcpy(asession_temp->sessid, p3, t->proxy->appsession_len); + asession_temp->sessid[t->proxy->appsession_len] = 0; + asession_temp->serverid = NULL; + + /* only do insert, if lookup fails */ + if (chtbl_lookup(&(t->proxy->htbl_proxy), (void *) &asession_temp) != 0) { + if ((asession_temp = pool_alloc(appsess)) == NULL) { + Alert("Not enought Memory process_srv():asession:calloc().\n"); + send_log(t->proxy, LOG_ALERT, "Not enought Memory process_srv():asession:calloc().\n"); + return 0; + } + asession_temp->sessid = local_asession.sessid; + asession_temp->serverid = local_asession.serverid; + chtbl_insert(&(t->proxy->htbl_proxy), (void *) asession_temp); + }/* end if(chtbl_lookup()) */ + else + { + /* free wasted memory */ + pool_free_to(apools.sessid, local_asession.sessid); + } /* end else from if(chtbl_lookup()) */ + + if(asession_temp->serverid == NULL){ + if((asession_temp->serverid = pool_alloc_from(apools.serverid, apools.ser_msize)) == NULL){ + Alert("Not enought Memory process_srv():asession->sessid:malloc().\n"); + send_log(t->proxy, LOG_ALERT, "Not enought Memory process_srv():asession->sessid:malloc().\n"); + } + asession_temp->serverid[0] = '\0'; + } + + if(asession_temp->serverid[0] == '\0') memcpy(asession_temp->serverid,t->srv->id,server_id_len); + + tv_delayfrom(&asession_temp->expire, &now, t->proxy->appsession_timeout); + +#if defined(DEBUG_HASH) + print_table(&(t->proxy->htbl_proxy)); +#endif + break; + }/* end if ((t->proxy->appsession_name != NULL) ... */ else { // fprintf(stderr,"Ignoring unknown cookie : "); // write(2, p1, p2-p1); @@ -4830,6 +5095,39 @@ void dump(int sig) { } } +static void fast_stop(void) +{ + struct proxy *p; + p = proxy; + while (p) { + p->grace = 0; + p = p->next; + } + soft_stop(); +} + +void sig_int(int sig) { + /* This would normally be a hard stop, + but we want to be sure about deallocation, + and so on, so we do a soft stop with + 0 GRACE time + */ + fast_stop(); + /* If we are killed twice, we decide to die*/ + signal(sig, SIG_DFL); +} + +void sig_term(int sig) { + /* This would normally be a hard stop, + but we want to be sure about deallocation, + and so on, so we do a soft stop with + 0 GRACE time + */ + fast_stop(); + /* If we are killed twice, we decide to die*/ + signal(sig, SIG_DFL); +} + void chain_regex(struct hdr_exp **head, regex_t *preg, int action, char *replace) { struct hdr_exp *exp; @@ -5003,6 +5301,7 @@ void init_default_instance() { int cfg_parse_listen(char *file, int linenum, char **args) { static struct proxy *curproxy = NULL; struct server *newsrv = NULL; + int rc; if (!strcmp(args[0], "listen")) { /* new proxy */ if (!*args[1]) { @@ -5195,7 +5494,36 @@ int cfg_parse_listen(char *file, int linenum, char **args) { file, linenum); return -1; } - } + }/* end else if (!strcmp(args[0], "cookie")) */ + else if (!strcmp(args[0], "appsession")) { /* cookie name */ +// if (curproxy == &defproxy) { +// Alert("parsing [%s:%d] : '%s' not allowed in 'defaults' section.\n", file, linenum, args[0]); +// return -1; +// } + + if (curproxy->appsession_name != NULL) { +// Alert("parsing [%s:%d] : cookie name already specified. Continuing.\n", +// file, linenum); +// return 0; + free(curproxy->appsession_name); + } + + if (*(args[5]) == 0) { + Alert("parsing [%s:%d] : '%s' expects 'appsession' 'len' 'timeout' .\n", + file, linenum, args[0]); + return -1; + } + have_appsession = 1; + curproxy->appsession_name = strdup(args[1]); + curproxy->appsession_name_len = strlen(curproxy->appsession_name); + curproxy->appsession_len = atoi(args[3]); + curproxy->appsession_timeout = atoi(args[5]); + rc = chtbl_init(&(curproxy->htbl_proxy), TBLSIZ, hashpjw, match_str, destroy); + if (rc) { + Alert("Error Init Appsession Hashtable.\n"); + return -1; + } + } /* Url App Session */ else if (!strcmp(args[0], "capture")) { if (!strcmp(args[1], "cookie")) { /* name of a cookie to capture */ // if (curproxy == &defproxy) { @@ -6438,10 +6766,13 @@ void init(int argc, char **argv) { gethostname(hostname, MAX_HOSTNAME_LEN); + have_appsession = 0; if (readcfgfile(cfg_cfgfile) < 0) { Alert("Error reading configuration file : %s\n", cfg_cfgfile); exit(1); } + if (have_appsession) + appsession_init(); if (global.mode & MODE_CHECK) { qfprintf(stdout, "Configuration file is valid : %s\n", cfg_cfgfile); @@ -6576,6 +6907,154 @@ int start_proxies() { return 0; } +int match_str(const void *key1, const void *key2){ + + appsess *temp1,*temp2; + temp1 = (appsess *)key1; + temp2 = (appsess *)key2; + + //fprintf(stdout,">>>>>>>>>>>>>>temp1->sessid :%s:\n",temp1->sessid); + //fprintf(stdout,">>>>>>>>>>>>>>temp2->sessid :%s:\n",temp2->sessid); + + return (strcmp(temp1->sessid,temp2->sessid) == 0); +}/* end match_str */ + +void destroy(void *data){ + appsess *temp1; + + //printf("destroy called\n"); + temp1 = (appsess *)data; + + if (temp1->sessid) + pool_free_to(apools.sessid, temp1->sessid); + + if (temp1->serverid) + pool_free_to(apools.serverid, temp1->serverid); + + pool_free(appsess, temp1); +} /* end destroy */ + +void appsession_cleanup( void ) +{ + struct proxy *p = proxy; + + while(p) { + chtbl_destroy(&(p->htbl_proxy)); + p = p->next; + } +}/* end appsession_cleanup() */ + +void pool_destroy(void **pool) +{ + void *temp, *next; + next = pool; + while (next) { + temp = next; + next = *(void **)temp; + free(temp); + } +}/* end pool_destroy() */ + +void deinit(void){ + struct proxy *p = proxy; + struct cap_hdr *h,*h_next; + struct server *s,*s_next; + struct listener *l,*l_next; + + while (p) { + if (p->id) + free(p->id); + + if (p->check_req) + free(p->check_req); + + if (p->cookie_name) + free(p->cookie_name); + + if (p->capture_name) + free(p->capture_name); + + /* only strup if the user have set in config. + When should we free it?! + if(p->errmsg.msg400) free(p->errmsg.msg400); + if(p->errmsg.msg403) free(p->errmsg.msg403); + if(p->errmsg.msg408) free(p->errmsg.msg408); + if(p->errmsg.msg500) free(p->errmsg.msg500); + if(p->errmsg.msg502) free(p->errmsg.msg502); + if(p->errmsg.msg503) free(p->errmsg.msg503); + if(p->errmsg.msg504) free(p->errmsg.msg504); + */ + if (p->appsession_name) + free(p->appsession_name); + + h = p->req_cap; + while (h) { + h_next = h->next; + if (h->name) + free(h->name); + pool_destroy(h->pool); + free(h); + h = h_next; + }/* end while(h) */ + + h = p->rsp_cap; + while (h) { + h_next = h->next; + if (h->name) + free(h->name); + + pool_destroy(h->pool); + free(h); + h = h_next; + }/* end while(h) */ + + s = p->srv; + while (s) { + s_next = s->next; + if(s->id) + free(s->id); + + if(s->cookie) + free(s->cookie); + + free(s); + s = s_next; + }/* end while(s) */ + + l = p->listen; + while (l) { + l_next = l->next; + free(l); + l = l_next; + }/* end while(l) */ + + pool_destroy((void **) p->req_cap_pool); + pool_destroy((void **) p->rsp_cap_pool); + p = p->next; + }/* end while(p) */ + + if (global.chroot) free(global.chroot); + if (global.pidfile) free(global.pidfile); + + if (ReadEvent) free(ReadEvent); + if (WriteEvent) free(WriteEvent); + if (StaticReadEvent) free(StaticReadEvent); + if (StaticWriteEvent) free(StaticWriteEvent); + if (fdtab) free(fdtab); + + pool_destroy(pool_session); + pool_destroy(pool_buffer); + pool_destroy(pool_fdtab); + pool_destroy(pool_requri); + pool_destroy(pool_task); + pool_destroy(pool_capture); + pool_destroy(pool_appsess); + + if (have_appsession) { + pool_destroy(apools.serverid); + pool_destroy(apools.sessid); + } +} /* end deinit() */ int main(int argc, char **argv) { FILE *pidfile = NULL; @@ -6590,6 +7069,8 @@ int main(int argc, char **argv) { signal(SIGQUIT, dump); signal(SIGUSR1, sig_soft_stop); signal(SIGHUP, sig_dump_state); + signal(SIGINT, sig_int); + signal(SIGTERM, sig_term); /* on very high loads, a sigpipe sometimes happen just between the * getsockopt() which tells "it's OK to write", and the following write :-( @@ -6676,5 +7157,152 @@ int main(int argc, char **argv) { select_loop(); + /* Free all Hash Keys and all Hash elements */ + appsession_cleanup(); + /* Do some cleanup */ + deinit(); + exit(0); } + +#if defined(DEBUG_HASH) +static void print_table(const CHTbl *htbl) { + + ListElmt *element; + int i; + appsess *asession; + + /***************************************************************************** + * * + * Display the chained hash table. * + * * + *****************************************************************************/ + + fprintf(stdout, "Table size is %d\n", chtbl_size(htbl)); + + for (i = 0; i < TBLSIZ; i++) { + fprintf(stdout, "Bucket[%03d]\n", i); + + for (element = list_head(&htbl->table[i]); element != NULL; element = list_next(element)) { + //fprintf(stdout, "%c", *(char *)list_data(element)); + asession = (appsess *)list_data(element); + fprintf(stdout, "ELEM :%s:", asession->sessid); + fprintf(stdout, " Server :%s: \n", asession->serverid); + //fprintf(stdout, " Server request_count :%li:\n",asession->request_count); + } + + fprintf(stdout, "\n"); + } + return; +} /* end print_table */ +#endif + +static int appsession_init(void) +{ + static int initialized = 0; + int idlen; + struct server *s; + struct proxy *p = proxy; + + if (!initialized) { + if (!appsession_task_init()) { + apools.sessid = NULL; + apools.serverid = NULL; + apools.ser_waste = 0; + apools.ser_use = 0; + apools.ser_msize = sizeof(void *); + apools.ses_waste = 0; + apools.ses_use = 0; + apools.ses_msize = sizeof(void *); + while (p) { + s = p->srv; + if (apools.ses_msize < p->appsession_len) + apools.ses_msize = p->appsession_len; + while (s) { + idlen = strlen(s->id); + if (apools.ser_msize < idlen) + apools.ser_msize = idlen; + s = s->next; + } + p = p->next; + } + apools.ser_msize ++; /* we use strings, so reserve space for '\0' */ + apools.ses_msize ++; + } + else { + fprintf(stderr, "appsession_task_init failed\n"); + return -1; + } + initialized ++; + } + return 0; +} + +static int appsession_task_init(void) +{ + static int initialized = 0; + struct task *t; + if (!initialized) { + if ((t = pool_alloc(task)) == NULL) + return -1; + t->next = t->prev = t->rqnext = NULL; + t->wq = LIST_HEAD(wait_queue); + t->state = TASK_IDLE; + t->context = NULL; + tv_delayfrom(&t->expire, &now, TBLCHKINT); + task_queue(t); + t->process = appsession_refresh; + initialized ++; + } + return 0; +} + +static int appsession_refresh(struct task *t) { + struct proxy *p = proxy; + CHTbl *htbl; + ListElmt *element, *last; + int i; + appsess *asession; + void *data; + + while (p) { + if (p->appsession_name != NULL) { + htbl = &p->htbl_proxy; + /* if we ever give up the use of TBLSIZ, we need to change this */ + for (i = 0; i < TBLSIZ; i++) { + last = NULL; + for (element = list_head(&htbl->table[i]); element != NULL; element = list_next(element)) { + asession = (appsess *)list_data(element); + if (tv_cmp2_ms(&asession->expire, &now) <= 0) { + if ((global.mode & MODE_DEBUG) && (!(global.mode & MODE_QUIET) || (global.mode & MODE_VERBOSE))) { + int len; + /* + on Linux NULL pointers are catched by sprintf, on solaris -> segfault + */ + len = sprintf(trash, "appsession_refresh: cleaning up expired Session '%s' on Server %s\n", + asession->sessid, asession->serverid?asession->serverid:"(null)"); + write(1, trash, len); + } + /* delete the expired element from within the hash table */ + if ((list_rem_next(&htbl->table[i], last, (void **)&data) == 0) + && (htbl->table[i].destroy != NULL)) { + htbl->table[i].destroy(data); + } + if (last == NULL) {/* patient lost his head, get a new one */ + element = list_head(&htbl->table[i]); + if (element == NULL) break; /* no heads left, go to next patient */ + } + else + element = last; + }/* end if (tv_cmp2_ms(&asession->expire, &now) <= 0) */ + else + last = element; + }/* end for (element = list_head(&htbl->table[i]); element != NULL; element = list_next(element)) */ + } + } + p = p->next; + } + tv_delayfrom(&t->expire, &now, TBLCHKINT); /* check expiration every 5 seconds */ + return TBLCHKINT; +} /* end appsession_refresh */ + diff --git a/include/chtbl.h b/include/chtbl.h new file mode 100644 index 000000000..87b777ac1 --- /dev/null +++ b/include/chtbl.h @@ -0,0 +1,62 @@ +/* + This File is copied from + + http://www.oreilly.com/catalog/masteralgoc/index.html + Mastering Algorithms with C + By Kyle Loudon + ISBN: 1-56592-453-3 + Publishd by O'Reilly + + */ + +/***************************************************************************** +* * +* ------------------------------- chtbl.h -------------------------------- * +* * +*****************************************************************************/ + +#ifndef CHTBL_H +#define CHTBL_H + +#include + +#include "list.h" + +/***************************************************************************** +* * +* Define a structure for chained hash tables. * +* * +*****************************************************************************/ + +typedef struct CHTbl_ { + + int buckets; + + int (*h)(const void *key); + int (*match)(const void *key1, const void *key2); + void (*destroy)(void *data); + + int size; + List *table; +} CHTbl; + +/***************************************************************************** + * * + * --------------------------- Public Interface --------------------------- * + * * + *****************************************************************************/ + +int chtbl_init(CHTbl *htbl, int buckets, int (*h)(const void *key), int + (*match)(const void *key1, const void *key2), void (*destroy)(void *data)); + +void chtbl_destroy(CHTbl *htbl); + +int chtbl_insert(CHTbl *htbl, const void *data); + +int chtbl_remove(CHTbl *htbl, void **data); + +int chtbl_lookup(const CHTbl *htbl, void **data); + +#define chtbl_size(htbl) ((htbl)->size) + +#endif diff --git a/include/hashpjw.h b/include/hashpjw.h new file mode 100644 index 000000000..a20b5b3b2 --- /dev/null +++ b/include/hashpjw.h @@ -0,0 +1,47 @@ +/* + This File is copied from + + http://www.oreilly.com/catalog/masteralgoc/index.html + Mastering Algorithms with C + By Kyle Loudon + ISBN: 1-56592-453-3 + Publishd by O'Reilly + + We have added our own struct to these function. + */ + +/***************************************************************************** +* * +* ------------------------------- hashpjw.h ------------------------------ * +* * +*****************************************************************************/ + +#ifndef HASHPJW_H +#define HASHPJW_H + +#include + +typedef struct appsessions { + char *sessid; + char *serverid; + struct timeval expire; /* next expiration time for this application session */ + unsigned long int request_count; +} appsess; /* end struct appsessions */ + +/***************************************************************************** +* * +* Define a table size for demonstration purposes only. * +* * +*****************************************************************************/ + +#define PRIME_TBLSIZ 1699 + +/***************************************************************************** +* * +* --------------------------- Public Interface --------------------------- * +* * +*****************************************************************************/ + +int hashpjw(const void *key); + +#endif diff --git a/include/list.h b/include/list.h new file mode 100644 index 000000000..ae7f0868e --- /dev/null +++ b/include/list.h @@ -0,0 +1,78 @@ +/* + This File is copied from + + http://www.oreilly.com/catalog/masteralgoc/index.html + Mastering Algorithms with C + By Kyle Loudon + ISBN: 1-56592-453-3 + Publishd by O'Reilly + + */ + +/***************************************************************************** +* * +* -------------------------------- list.h -------------------------------- * +* * +*****************************************************************************/ + +#ifndef LIST_H +#define LIST_H + +#include + +/***************************************************************************** + * * + * Define a structure for linked list elements. * + * * + *****************************************************************************/ + +typedef struct ListElmt_ { + + void *data; + struct ListElmt_ *next; +} ListElmt; + +/***************************************************************************** + * * + * Define a structure for linked lists. * + * * + *****************************************************************************/ + +typedef struct List_ { + int size; + int (*match)(const void *key1, const void *key2); + void (*destroy)(void *data); + + ListElmt *head; + ListElmt *tail; +} List; + +/***************************************************************************** + * * + * --------------------------- Public Interface --------------------------- * + * * + *****************************************************************************/ + +void list_init(List *list, void (*destroy)(void *data)); + +void list_destroy(List *list); + +int list_ins_next(List *list, ListElmt *element, const void *data); + +int list_rem_next(List *list, ListElmt *element, void **data); + +#define list_size(list) ((list)->size) + +#define list_head(list) ((list)->head) + +#define list_tail(list) ((list)->tail) + +#define list_is_head(list, element) ((element) == (list)->head ? 1 : 0) + +#define list_is_tail(element) ((element)->next == NULL ? 1 : 0) + +#define list_data(element) ((element)->data) + +#define list_next(element) ((element)->next) + +#endif diff --git a/src/chtbl.c b/src/chtbl.c new file mode 100644 index 000000000..481b3ac63 --- /dev/null +++ b/src/chtbl.c @@ -0,0 +1,260 @@ +/* + This File is copied from + + http://www.oreilly.com/catalog/masteralgoc/index.html + Mastering Algorithms with C + By Kyle Loudon + ISBN: 1-56592-453-3 + Publishd by O'Reilly + + */ + +/***************************************************************************** + * * + * ------------------------------- chtbl.c -------------------------------- * + * * + *****************************************************************************/ + +#include +#include + +#include +#include + +/***************************************************************************** + * * + * ------------------------------ chtbl_init ------------------------------ * + * * + *****************************************************************************/ + +int chtbl_init(CHTbl *htbl, int buckets, int (*h)(const void *key), int + (*match)(const void *key1, const void *key2), void (*destroy)(void*data)) { + + int i; + + /***************************************************************************** + * * + * Allocate space for the hash table. * + * * + *****************************************************************************/ + + if ((htbl->table = (List *)malloc(buckets * sizeof(List))) == NULL) + return -1; + + /***************************************************************************** + * * + * Initialize the buckets. * + * * + *****************************************************************************/ + + htbl->buckets = buckets; + + for (i = 0; i < htbl->buckets; i++) + list_init(&htbl->table[i], destroy); + + /***************************************************************************** + * * + * Encapsulate the functions. * + * * + *****************************************************************************/ + + htbl->h = h; + htbl->match = match; + htbl->destroy = destroy; + + /***************************************************************************** + * * + * Initialize the number of elements in the table. * + * * + *****************************************************************************/ + + htbl->size = 0; + + return 0; +} /* end chtbl_init () */ + +/***************************************************************************** + * * + * ---------------------------- chtbl_destroy ----------------------------- * + * * + *****************************************************************************/ + +void chtbl_destroy(CHTbl *htbl) { + + int i; + + /***************************************************************************** + * * + * Destroy each bucket. * + * * + *****************************************************************************/ + + for (i = 0; i < htbl->buckets; i++) { + list_destroy(&htbl->table[i]); + } /* end for () */ + + /***************************************************************************** + * * + * Free the storage allocated for the hash table. * + * * + *****************************************************************************/ + + free(htbl->table); + + /***************************************************************************** + * * + * No operations are allowed now, but clear the structure as a precaution. * + * * + *****************************************************************************/ + + memset(htbl, 0, sizeof(CHTbl)); + + return; +} /* end chtbl_destroy() */ + +/***************************************************************************** + * * + * ----------------------------- chtbl_insert ----------------------------- * + * * + *****************************************************************************/ + +int chtbl_insert(CHTbl *htbl, const void *data) { + + void *temp; + int bucket,retval; + + /***************************************************************************** + * * + * Do nothing if the data is already in the table. * + * * + *****************************************************************************/ + + temp = (void *)data; + + if (chtbl_lookup(htbl, &temp) == 0) + return 1; + + /***************************************************************************** + * * + * Hash the key. * + * * + *****************************************************************************/ + + bucket = htbl->h(data) % htbl->buckets; + + /***************************************************************************** + * * + * Insert the data into the bucket. * + * * + *****************************************************************************/ + + if ((retval = list_ins_next(&htbl->table[bucket], NULL, data)) == 0) + htbl->size++; + + return retval; +} /* end chtbl_insert() */ + +/***************************************************************************** + * * + * ----------------------------- chtbl_remove ----------------------------- * + * * + *****************************************************************************/ + +int chtbl_remove(CHTbl *htbl, void **data) { + + ListElmt *element, *prev; + int bucket; + + /***************************************************************************** + * * + * Hash the key. * + * * + *****************************************************************************/ + + bucket = htbl->h(*data) % htbl->buckets; + + /***************************************************************************** + * * + * Search for the data in the bucket. * + * * + *****************************************************************************/ + + prev = NULL; + + for (element = list_head(&htbl->table[bucket]); element != NULL; element = list_next(element)) { + if (htbl->match(*data, list_data(element))) { + + /*********************************************************************** + * * + * Remove the data from the bucket. * + * * + ***********************************************************************/ + + if (list_rem_next(&htbl->table[bucket], prev, data) == 0) { + htbl->size--; + return 0; + } /* end if() */ + else { + return -1; + }/* end else */ + }/* end if (htbl->match(*data, list_data(element))) */ + + prev = element; + }/* end for() */ + + /***************************************************************************** + * * + * Return that the data was not found. * + * * + *****************************************************************************/ + + return -1; +} /* end int chtbl_remove(CHTbl *htbl, void **data) */ + +/***************************************************************************** + * * + * ----------------------------- chtbl_lookup ----------------------------- * + * * + *****************************************************************************/ + +int chtbl_lookup(const CHTbl *htbl, void **data) { + + ListElmt *element; + int bucket; + + /***************************************************************************** + * * + * Hash the key. * + * * + *****************************************************************************/ + + bucket = htbl->h(*data) % htbl->buckets; + + /***************************************************************************** + * * + * Search for the data in the bucket. * + * * + *****************************************************************************/ + + for (element = list_head(&htbl->table[bucket]); element != NULL; element = list_next(element)) { + if (htbl->match(*data, list_data(element))) { + + /*********************************************************************** + * * + * Pass back the data from the table. * + * * + ***********************************************************************/ + + *data = list_data(element); + return 0; + }/* end if() */ + }/* end for() */ + + /***************************************************************************** + * * + * Return that the data was not found. * + * * + *****************************************************************************/ + + return -1; +} /* end chtbl_lookup() */ diff --git a/src/hashpjw.c b/src/hashpjw.c new file mode 100644 index 000000000..ef7f209b1 --- /dev/null +++ b/src/hashpjw.c @@ -0,0 +1,62 @@ +/* + This File is copied from + + http://www.oreilly.com/catalog/masteralgoc/index.html + Mastering Algorithms with C + By Kyle Loudon + ISBN: 1-56592-453-3 + Publishd by O'Reilly + + We have added our own struct to these function. + */ + +/***************************************************************************** +* * +* ------------------------------- hashpjw.c ------------------------------ * +* * +*****************************************************************************/ + +#include + +/***************************************************************************** +* * +* -------------------------------- hashpjw ------------------------------- * +* * +*****************************************************************************/ + +int hashpjw(const void *key) { + + const char *ptr; + unsigned int val; + AppSess *appsession_temp; + + /***************************************************************************** + * * + * Hash the key by performing a number of bit operations on it. * + * * + *****************************************************************************/ + + val = 0; + appsession_temp = (AppSess *)key; + ptr = appsession_temp->sessid; + + while (*ptr != '\0') { + + int tmp; + + val = (val << 4) + (*ptr); + + if((tmp = (val & 0xf0000000))) { + val = val ^ (tmp >> 24); + val = val ^ tmp; + } + ptr++; + }/* end while */ + + /***************************************************************************** + * * + * In practice, replace PRIME_TBLSIZ with the actual table size. * + * * + *****************************************************************************/ + return val % PRIME_TBLSIZ; +}/* end hashpjw */ diff --git a/src/list.c b/src/list.c new file mode 100644 index 000000000..364ed141f --- /dev/null +++ b/src/list.c @@ -0,0 +1,228 @@ +/* + This File is copied from + + http://www.oreilly.com/catalog/masteralgoc/index.html + Mastering Algorithms with C + By Kyle Loudon + ISBN: 1-56592-453-3 + Publishd by O'Reilly + + */ + +/***************************************************************************** + * * + * -------------------------------- list.c -------------------------------- * + * * + *****************************************************************************/ + +#include +#include + +#include + +/***************************************************************************** +* * +* ------------------------------- list_init ------------------------------ * +* * +*****************************************************************************/ + +void list_init(List *list, void (*destroy)(void *data)) { + + /***************************************************************************** + * * + * Initialize the list. * + * * + *****************************************************************************/ + + list->size = 0; + list->destroy = destroy; + list->head = NULL; + list->tail = NULL; + return; +} /* end list_init() */ + +/***************************************************************************** + * * + * ----------------------------- list_destroy ----------------------------- * + * * + *****************************************************************************/ + +void list_destroy(List *list) { + + void *data; + int rc; + + /***************************************************************************** + * * + * Remove each element. * + * * + *****************************************************************************/ + + while (list_size(list) > 0) { + + rc = list_rem_next(list, NULL, (void **)&data); + + if (( rc == 0) && (list->destroy != NULL)) { + + /*********************************************************************** + * * + * Call a user-defined function to free dynamically allocated data. * + * * + ***********************************************************************/ + + list->destroy(data); + }/* end if() */ + }/* end while() */ + + /***************************************************************************** + * * + * No operations are allowed now, but clear the structure as a precaution. * + * * + *****************************************************************************/ + + memset(list, 0, sizeof(List)); + return; +} /* void list_destroy(List *list) */ + +/***************************************************************************** + * * + * ----------------------------- list_ins_next ---------------------------- * + * * + *****************************************************************************/ + +int list_ins_next(List *list, ListElmt *element, const void *data) { + + ListElmt *new_element; + + /***************************************************************************** + * * + * Allocate storage for the element. * + * * + *****************************************************************************/ + + if ((new_element = (ListElmt *)malloc(sizeof(ListElmt))) == NULL) + return -1; + + /***************************************************************************** + * * + * Insert the element into the list. * + * * + *****************************************************************************/ + + new_element->data = (void *)data; + + if (element == NULL) { + + /************************************************************************** + * * + * Handle insertion at the head of the list. * + * * + **************************************************************************/ + + if (list_size(list) == 0) + list->tail = new_element; + + new_element->next = list->head; + list->head = new_element; + }/* end if (element == NULL) */ + else { + + /************************************************************************** + * * + * Handle insertion somewhere other than at the head. * + * * + **************************************************************************/ + + if (element->next == NULL) + list->tail = new_element; + + new_element->next = element->next; + element->next = new_element; + }/* end else */ + + /***************************************************************************** + * * + * Adjust the size of the list to account for the inserted element. * + * * + *****************************************************************************/ + + list->size++; + return 0; +} /* end list_ins_next() */ + +/***************************************************************************** + * * + * ----------------------------- list_rem_next ---------------------------- * + * * + *****************************************************************************/ + +int list_rem_next(List *list, ListElmt *element, void **data) { + + ListElmt *old_element; + + /***************************************************************************** + * * + * Do not allow removal from an empty list. * + * * + *****************************************************************************/ + + if (list_size(list) == 0) + return -1; + + /***************************************************************************** + * * + * Remove the element from the list. * + * * + *****************************************************************************/ + + if (element == NULL) { + + /************************************************************************** + * * + * Handle removal from the head of the list. * + * * + **************************************************************************/ + + *data = list->head->data; + old_element = list->head; + list->head = list->head->next; + + if (list_size(list) == 1) + list->tail = NULL; + }/* end if (element == NULL) */ + else { + + /************************************************************************** + * * + * Handle removal from somewhere other than the head. * + * * + **************************************************************************/ + + if (element->next == NULL) + return -1; + + *data = element->next->data; + old_element = element->next; + element->next = element->next->next; + + if (element->next == NULL) + list->tail = element; + }/* end else */ + + /***************************************************************************** + * * + * Free the storage allocated by the abstract data type. * + * * + *****************************************************************************/ + + free(old_element); + + /***************************************************************************** + * * + * Adjust the size of the list to account for the removed element. * + * * + *****************************************************************************/ + + list->size--; + return 0; +}