diff --git a/NOTES b/NOTES new file mode 100644 index 000000000..d46219562 --- /dev/null +++ b/NOTES @@ -0,0 +1,13 @@ +1.1.5 -> 1.1.6 + * added reqdeny / reqallow rules + * added HTTP 400 and 403 responses + * chain regex in a list + * reply 502 when no server is available +1.1.6 -> 1.1.7 + - implement global logging + - have a single log function + - handle parametrable HTTP health-checks replies + - differentiate http headers and http uris + - log http requests on demand, and destination server IP + - add x-forwarded-for + diff --git a/examples/rc.highsock b/examples/rc.highsock index 76875eaec..9325ee062 100644 --- a/examples/rc.highsock +++ b/examples/rc.highsock @@ -8,12 +8,12 @@ if [ -e /proc/sys/net/ipv4/ip_conntrack_max ]; then echo 65536 > /proc/sys/net/ipv4/ip_conntrack_max fi -if [ -e /proc/sys/net/ipv4/netfilter/ip_ct_tcp_timeout_fin_wait ]; then +if [ -e /proc/sys/net/ipv4/netfilter/ip_conntrack_tcp_timeout_fin_wait ]; then # 30 seconds for fin, 15 for time wait - echo 3000 > /proc/sys/net/ipv4/netfilter/ip_ct_tcp_timeout_fin_wait - echo 1500 > /proc/sys/net/ipv4/netfilter/ip_ct_tcp_timeout_time_wait - echo 0 > /proc/sys/net/ipv4/netfilter/ip_ct_tcp_log_invalid_scale - echo 0 > /proc/sys/net/ipv4/netfilter/ip_ct_tcp_log_out_of_window + echo 3000 > /proc/sys/net/ipv4/netfilter/ip_conntrack_tcp_timeout_fin_wait + echo 1500 > /proc/sys/net/ipv4/netfilter/ip_conntrack_tcp_timeout_time_wait + echo 0 > /proc/sys/net/ipv4/netfilter/ip_conntrack_tcp_log_invalid_scale + echo 0 > /proc/sys/net/ipv4/netfilter/ip_conntrack_tcp_log_out_of_window fi echo 1024 60999 > /proc/sys/net/ipv4/ip_local_port_range diff --git a/haproxy.c b/haproxy.c index f2832de97..bc17e3ec6 100644 --- a/haproxy.c +++ b/haproxy.c @@ -13,21 +13,23 @@ * * ChangeLog : * - * 2002/04/03 - * - released 1.1.5 + * 2002/04/08 : 1.1.6 + * - regex are now chained and not limited anymore. + * - unavailable server now returns HTTP/502. + * - increased per-line args limit to 40 + * - added reqallow/reqdeny to block some request on matches + * - added HTTP 400/403 responses + * 2002/04/03 : 1.1.5 * - connection logging displayed incorrect source address. * - added proxy start/stop and server up/down log events. * - replaced log message short buffers with larger trash. * - enlarged buffer to 8 kB and replace buffer to 4 kB. - * 2002/03/25 - * - released 1.1.4 + * 2002/03/25 : 1.1.4 * - made rise/fall/interval time configurable - * 2002/03/22 - * - released 1.1.3 + * 2002/03/22 : 1.1.3 * - fixed a bug : cr_expire and cw_expire were inverted in CL_STSHUT[WR] * which could lead to loops. - * 2002/03/21 - * - released 1.1.2 + * 2002/03/21 : 1.1.2 * - fixed a bug in buffer management where we could have a loop * between event_read() and process_{cli|srv} if R==BUFSIZE-MAXREWRITE. * => implemented an adjustable buffer limit. @@ -35,12 +37,10 @@ * and running tasks are skipped. * - added some debug lines for accept events. * - send warnings for servers up/down. - * 2002/03/12 - * - released 1.1.1 + * 2002/03/12 : 1.1.1 * - fixed a bug in total failure handling * - fixed a bug in timestamp comparison within same second (tv_cmp_ms) - * 2002/03/10 - * - released 1.1.0 + * 2002/03/10 : 1.1.0 * - fixed a few timeout bugs * - rearranged the task scheduler subsystem to improve performance, * add new tasks, and make it easier to later port to librt ; @@ -108,8 +108,8 @@ #include #endif -#define HAPROXY_VERSION "1.1.5" -#define HAPROXY_DATE "2002/04/03" +#define HAPROXY_VERSION "1.1.6pre4" +#define HAPROXY_DATE "2002/04/07" /* this is for libc5 for example */ #ifndef TCP_NODELAY @@ -130,10 +130,10 @@ #define MAXREWRITE 4096 // max # args on a configuration line -#define MAX_LINE_ARGS 10 +#define MAX_LINE_ARGS 40 -// max # of regexps per proxy -#define MAX_REGEXP 10 +// max # of added headers per request +#define MAX_NEWHDR 10 // max # of matches per regexp #define MAX_MATCH 10 @@ -271,8 +271,12 @@ int strlcpy(char *dst, const char *src, int size) { #define PR_O_BALANCE_RR 32 /* balance in round-robin mode */ #define PR_O_BALANCE (PR_O_BALANCE_RR) -/* various task flags */ -#define TF_DIRECT 1 /* connection made on the server matching the client cookie */ +/* various session flags */ +#define SN_DIRECT 1 /* connection made on the server matching the client cookie */ +#define SN_CLDENY 2 /* a client header matches a deny regex */ +#define SN_CLALLOW 4 /* a client header matches an allow regex */ +#define SN_SVDENY 8 /* a server header matches a deny regex */ +#define SN_SVALLOW 16 /* a server header matches an allow regex */ /* different possible states for the client side */ #define CL_STHEADERS 0 @@ -306,6 +310,12 @@ int strlcpy(char *dst, const char *src, int size) { /* server flags */ #define SRV_RUNNING 1 +/* what to do when a header matches a regex */ +#define ACT_ALLOW 0 /* allow the request */ +#define ACT_REPLACE 1 /* replace the matching header */ +#define ACT_REMOVE 2 /* remove the matching header */ +#define ACT_DENY 3 /* deny the request */ + /*********************************************************************/ #define LIST_HEAD(a) ((void *)(&(a))) @@ -313,8 +323,10 @@ int strlcpy(char *dst, const char *src, int size) { /*********************************************************************/ struct hdr_exp { - regex_t *preg; /* expression to look for */ - char *replace; /* expression to set instead */ + struct hdr_exp *next; + regex_t *preg; /* expression to look for */ + int action; /* ACT_ALLOW, ACT_REPLACE, ACT_REMOVE, ACT_DENY */ + char *replace; /* expression to set instead */ }; struct buffer { @@ -395,10 +407,10 @@ struct proxy { struct sockaddr_in logsrv1, logsrv2; /* 2 syslog servers */ char logfac1, logfac2; /* log facility for both servers. -1 = disabled */ struct timeval stop_time; /* date to stop listening, when stopping != 0 */ - int nb_reqexp, nb_rspexp, nb_reqadd, nb_rspadd; - struct hdr_exp req_exp[MAX_REGEXP]; /* regular expressions for request headers */ - struct hdr_exp rsp_exp[MAX_REGEXP]; /* regular expressions for response headers */ - char *req_add[MAX_REGEXP], *rsp_add[MAX_REGEXP]; /* headers to be added */ + int nb_reqadd, nb_rspadd; + struct hdr_exp *req_exp; /* regular expressions for request headers */ + struct hdr_exp *rsp_exp; /* regular expressions for response headers */ + char *req_add[MAX_NEWHDR], *rsp_add[MAX_NEWHDR]; /* headers to be added */ int grace; /* grace time after stop request */ }; @@ -479,6 +491,27 @@ const char *monthname[12] = {"Jan", "Feb", "Mar", "Apr", "May", "Jun", #define MAX_HOSTNAME_LEN 32 static char hostname[MAX_HOSTNAME_LEN] = ""; +const char *HTTP_403 = + "HTTP/1.0 403 Forbidden\r\n" + "Cache-Control: no-cache\r\n" + "Connection: close\r\n" + "\r\n" + "403 Forbidden : Request forbidden by administrative rules.\r\n"; + +const char *HTTP_400 = + "HTTP/1.0 400 Bad request\r\n" + "Cache-Control: no-cache\r\n" + "Connection: close\r\n" + "\r\n" + "400 Bad request : Your browser sent an invalid request.\r\n"; + +const char *HTTP_502 = + "HTTP/1.0 502 Proxy Error\r\n" + "Cache-Control: no-cache\r\n" + "Connection: close\r\n" + "\r\n" + "502 Proxy Error : No server is available to handle this request.\r\n"; + /*********************************************************************/ /* statistics ******************************************************/ /*********************************************************************/ @@ -1075,7 +1108,7 @@ int connect_server(struct session *s) { // fprintf(stderr,"connect_server : s=%p\n",s); - if (s->flags & TF_DIRECT) { /* srv cannot be null */ + if (s->flags & SN_DIRECT) { /* srv cannot be null */ s->srv_addr = s->srv->addr; } else if (s->proxy->options & PR_O_BALANCE) { @@ -1510,6 +1543,38 @@ int event_srv_write(int fd) { } +/* + * returns a message to the client ; the connection is shut down for read, + * and the request is cleared so that no server connection can be initiated. + * The client must be in a valid state for this (HEADER, DATA ...). + * Nothing is performed on the server side. + * The reply buffer must be empty before this. + */ +void client_retnclose(struct session *s, int len, const char *msg) { + FD_CLR(s->cli_fd, StaticReadEvent); + FD_SET(s->cli_fd, StaticWriteEvent); + tv_eternity(&s->crexpire); + shutdown(s->cli_fd, SHUT_RD); + s->cli_state = CL_STSHUTR; + strcpy(s->rep->data, msg); + s->rep->l = len; + s->rep->r += len; + s->req->l = 0; +} + + +/* + * returns a message into the rep buffer, and flushes the req buffer. + * The reply buffer must be empty before this. + */ +void client_return(struct session *s, int len, const char *msg) { + strcpy(s->rep->data, msg); + s->rep->l = len; + s->rep->r += len; + s->req->l = 0; +} + + /* * this function is called on a read event from a listen socket, corresponding * to an accept. It tries to accept as many connections as possible. @@ -1638,15 +1703,7 @@ int event_accept(int fd) { fdtab[cfd].state = FD_STREADY; if (p->mode == PR_MODE_HEALTH) { /* health check mode, no client reading */ - FD_CLR(cfd, StaticReadEvent); - FD_SET(cfd, StaticWriteEvent); - tv_eternity(&s->crexpire); - shutdown(s->cli_fd, SHUT_RD); - s->cli_state = CL_STSHUTR; - - strcpy(s->rep->data, "OK\n"); /* forge an "OK" response */ - s->rep->l = 3; - s->rep->r += 3; + client_retnclose(s, 3, "OK\n"); /* forge an "OK" response */ } else { FD_SET(cfd, StaticReadEvent); @@ -1835,6 +1892,12 @@ int process_cli(struct session *t) { /* we can only get here after an end of headers */ /* we'll have something else to do here : add new headers ... */ + if (t->flags & SN_CLDENY) { + /* no need to go further */ + client_retnclose(t, strlen(HTTP_403), HTTP_403); + return 1; + } + for (line = 0; line < t->proxy->nb_reqadd; line++) { len = sprintf(newhdr, "%s\r\n", t->proxy->req_add[line]); buffer_replace2(req, req->h, req->h, newhdr, len); @@ -1888,25 +1951,38 @@ int process_cli(struct session *t) { } /* try headers regexps */ - if (t->proxy->nb_reqexp) { - struct proxy *p = t->proxy; - int exp; + if (t->proxy->req_exp != NULL && !(t->flags & SN_CLDENY)) { + struct hdr_exp *exp; char term; term = *ptr; *ptr = '\0'; - for (exp=0; exp < p->nb_reqexp; exp++) { - if (regexec(p->req_exp[exp].preg, req->h, MAX_MATCH, pmatch, 0) == 0) { - if (p->req_exp[exp].replace != NULL) { - int len = exp_replace(trash, req->h, p->req_exp[exp].replace, pmatch); - ptr += buffer_replace2(req, req->h, ptr, trash, len); - } - else { - delete_header = 1; + exp = t->proxy->req_exp; + do { + if (regexec(exp->preg, req->h, MAX_MATCH, pmatch, 0) == 0) { + switch (exp->action) { + case ACT_ALLOW: + if (!(t->flags & SN_CLDENY)) + t->flags |= SN_CLALLOW; + break; + case ACT_REPLACE: + if (!(t->flags & SN_CLDENY)) { + int len = exp_replace(trash, req->h, exp->replace, pmatch); + ptr += buffer_replace2(req, req->h, ptr, trash, len); + } + break; + case ACT_REMOVE: + if (!(t->flags & SN_CLDENY)) + delete_header = 1; + break; + case ACT_DENY: + if (!(t->flags & SN_CLALLOW)) + t->flags |= SN_CLDENY; + break; } break; } - } + } while ((exp = exp->next) != NULL); *ptr = term; /* restore the string terminator */ } @@ -1965,7 +2041,7 @@ int process_cli(struct session *t) { } if (srv) { /* we found the server */ - t->flags |= TF_DIRECT; + t->flags |= SN_DIRECT; t->srv = srv; } @@ -1984,7 +2060,7 @@ int process_cli(struct session *t) { } /* end of cookie processing */ /* let's look if we have to delete this header */ - if (delete_header) { + if (delete_header && !(t->flags & SN_CLDENY)) { buffer_replace2(req, req->h, req->lr, "", 0); } req->h = req->lr; @@ -1992,7 +2068,6 @@ int process_cli(struct session *t) { /* end of header processing (even if incomplete) */ - if ((req->l < req->rlim - req->data) && ! FD_ISSET(t->cli_fd, StaticReadEvent)) { /* fd in StaticReadEvent was disabled, perhaps because of a previous buffer * full. We cannot loop here since event_cli_read will disable it only if @@ -2005,12 +2080,20 @@ int process_cli(struct session *t) { tv_eternity(&t->crexpire); } - /* read timeout, read error, or last read : give up. - * since we are in header mode, if there's no space left for headers, we + /* Since we are in header mode, if there's no space left for headers, we * won't be able to free more later, so the session will never terminate. */ - if (t->res_cr == RES_ERROR || t->res_cr == RES_NULL - || req->l >= req->rlim - req->data || tv_cmp2_ms(&t->crexpire, &now) <= 0) { + if (req->l >= req->rlim - req->data) { + client_retnclose(t, strlen(HTTP_400), HTTP_400); + return 1; + } + else if (t->res_cr == RES_ERROR || t->res_cr == RES_NULL + || tv_cmp2_ms(&t->crexpire, &now) <= 0) { + + /* read timeout, read error, or last read : give up. + * since we are in header mode, if there's no space left for headers, we + * won't be able to free more later, so the session will never terminate. + */ tv_eternity(&t->crexpire); fd_delete(t->cli_fd); t->cli_state = CL_STCLOSE; @@ -2186,7 +2269,7 @@ int process_srv(struct session *t) { else { /* try again */ while (t->conn_retries-- > 0) { if ((t->proxy->options & PR_O_REDISP) && (t->conn_retries == 0)) { - t->flags &= ~TF_DIRECT; /* ignore cookie and force to use the dispatcher */ + t->flags &= ~SN_DIRECT; /* ignore cookie and force to use the dispatcher */ t->srv = NULL; /* it's left to the dispatcher to choose a server */ } @@ -2199,6 +2282,7 @@ int process_srv(struct session *t) { /* if conn_retries < 0 or other error, let's abort */ tv_eternity(&t->cnexpire); t->srv_state = SV_STCLOSE; + client_return(t, strlen(HTTP_502), HTTP_502); } } return 1; @@ -2218,7 +2302,7 @@ int process_srv(struct session *t) { t->conn_retries--; if (t->conn_retries >= 0) { if ((t->proxy->options & PR_O_REDISP) && (t->conn_retries == 0)) { - t->flags &= ~TF_DIRECT; /* ignore cookie and force to use the dispatcher */ + t->flags &= ~SN_DIRECT; /* ignore cookie and force to use the dispatcher */ t->srv = NULL; /* it's left to the dispatcher to choose a server */ } if (connect_server(t) == 0) @@ -2274,7 +2358,7 @@ int process_srv(struct session *t) { /* we can only get here after an end of headers */ /* we'll have something else to do here : add new headers ... */ - if ((t->srv) && !(t->flags & TF_DIRECT) && (t->proxy->options & PR_O_COOK_INS)) { + if ((t->srv) && !(t->flags & SN_DIRECT) && (t->proxy->options & PR_O_COOK_INS)) { /* the server is known, it's not the one the client requested, we have to * insert a set-cookie here. */ @@ -2334,25 +2418,38 @@ int process_srv(struct session *t) { } /* try headers regexps */ - if (t->proxy->nb_rspexp) { - struct proxy *p = t->proxy; - int exp; + if (t->proxy->rsp_exp != NULL && !(t->flags & SN_SVDENY)) { + struct hdr_exp *exp; char term; term = *ptr; *ptr = '\0'; - for (exp=0; exp < p->nb_rspexp; exp++) { - if (regexec(p->rsp_exp[exp].preg, rep->h, MAX_MATCH, pmatch, 0) == 0) { - if (p->rsp_exp[exp].replace != NULL) { - int len = exp_replace(trash, rep->h, p->rsp_exp[exp].replace, pmatch); - ptr += buffer_replace2(rep, rep->h, ptr, trash, len); - } - else { - delete_header = 1; + exp = t->proxy->rsp_exp; + do { + if (regexec(exp->preg, rep->h, MAX_MATCH, pmatch, 0) == 0) { + switch (exp->action) { + case ACT_ALLOW: + if (!(t->flags & SN_SVDENY)) + t->flags |= SN_SVALLOW; + break; + case ACT_REPLACE: + if (!(t->flags & SN_SVDENY)) { + int len = exp_replace(trash, rep->h, exp->replace, pmatch); + ptr += buffer_replace2(rep, rep->h, ptr, trash, len); + } + break; + case ACT_REMOVE: + if (!(t->flags & SN_SVDENY)) + delete_header = 1; + break; + case ACT_DENY: + if (!(t->flags & SN_SVALLOW)) + t->flags |= SN_SVDENY; + break; } break; } - } + } while ((exp = exp->next) != NULL); *ptr = term; /* restore the string terminator */ } @@ -2401,7 +2498,7 @@ int process_srv(struct session *t) { * We'll delete it too if the "indirect" option is set and we're in * a direct access. */ if (((t->srv) && (t->proxy->options & PR_O_COOK_INS)) || - ((t->flags & TF_DIRECT) && (t->proxy->options & PR_O_COOK_IND))) { + ((t->flags & SN_DIRECT) && (t->proxy->options & PR_O_COOK_IND))) { /* this header must be deleted */ delete_header = 1; } @@ -2425,9 +2522,9 @@ int process_srv(struct session *t) { } /* end of cookie processing */ /* let's look if we have to delete this header */ - if (delete_header) { + if (delete_header && !(t->flags & SN_SVDENY)) buffer_replace2(rep, rep->h, rep->lr, "", 0); - } + rep->h = rep->lr; } /* while (rep->lr < rep->r) */ @@ -3142,6 +3239,20 @@ void dump(int sig) { } } +void chain_regex(struct hdr_exp **head, regex_t *preg, int action, char *replace) { + struct hdr_exp *exp; + + while (*head != NULL) + head = &(*head)->next; + + exp = calloc(1, sizeof(struct hdr_exp)); + + exp->preg = preg; + exp->replace = replace; + exp->action = action; + *head = exp; +} + /* * This function reads and parses the configuration file given in the argument. * returns 0 if OK, -1 if error. @@ -3525,11 +3636,6 @@ int readcfgfile(char *file) { } else if (!strcmp(args[0], "cliexp") || !strcmp(args[0], "reqrep")) { /* replace request header from a regex */ regex_t *preg; - if (curproxy->nb_reqexp >= MAX_REGEXP) { - Alert("parsing [%s:%d] : too many request expressions. Continuing.\n", - file, linenum); - continue; - } if (*(args[1]) == 0 || *(args[2]) == 0) { Alert("parsing [%s:%d] : expects and as arguments.\n", @@ -3542,20 +3648,14 @@ int readcfgfile(char *file) { Alert("parsing [%s:%d] : bad regular expression <%s>.\n", file, linenum, args[1]); return -1; } - curproxy->req_exp[curproxy->nb_reqexp].preg = preg; - curproxy->req_exp[curproxy->nb_reqexp].replace = strdup(args[2]); - curproxy->nb_reqexp++; + + chain_regex(&curproxy->req_exp, preg, ACT_REPLACE, strdup(args[2])); } else if (!strcmp(args[0], "reqdel")) { /* delete request header from a regex */ regex_t *preg; - if (curproxy->nb_reqexp >= MAX_REGEXP) { - Alert("parsing [%s:%d] : too many request expressions. Continuing.\n", - file, linenum); - continue; - } if (*(args[1]) == 0) { - Alert("parsing [%s:%d] : expects as an argument.\n", + Alert("parsing [%s:%d] : expects as an argument.\n", file, linenum); return -1; } @@ -3565,13 +3665,46 @@ int readcfgfile(char *file) { Alert("parsing [%s:%d] : bad regular expression <%s>.\n", file, linenum, args[1]); return -1; } - curproxy->req_exp[curproxy->nb_reqexp].preg = preg; - curproxy->req_exp[curproxy->nb_reqexp].replace = NULL; /* means it must be deleted */ - curproxy->nb_reqexp++; + + chain_regex(&curproxy->req_exp, preg, ACT_REMOVE, NULL); + } + else if (!strcmp(args[0], "reqdeny")) { /* deny a request if a header matches this regex */ + regex_t *preg; + + if (*(args[1]) == 0) { + Alert("parsing [%s:%d] : expects as an argument.\n", + file, linenum); + return -1; + } + + preg = calloc(1, sizeof(regex_t)); + if (regcomp(preg, args[1], REG_EXTENDED) != 0) { + Alert("parsing [%s:%d] : bad regular expression <%s>.\n", file, linenum, args[1]); + return -1; + } + + chain_regex(&curproxy->req_exp, preg, ACT_DENY, NULL); + } + else if (!strcmp(args[0], "reqallow")) { /* allow a request if a header matches this regex */ + regex_t *preg; + + if (*(args[1]) == 0) { + Alert("parsing [%s:%d] : expects as an argument.\n", + file, linenum); + return -1; + } + + preg = calloc(1, sizeof(regex_t)); + if (regcomp(preg, args[1], REG_EXTENDED) != 0) { + Alert("parsing [%s:%d] : bad regular expression <%s>.\n", file, linenum, args[1]); + return -1; + } + + chain_regex(&curproxy->req_exp, preg, ACT_ALLOW, NULL); } else if (!strcmp(args[0], "reqadd")) { /* add request header */ - if (curproxy->nb_reqadd >= MAX_REGEXP) { - Alert("parsing [%s:%d] : too many client expressions. Continuing.\n", + if (curproxy->nb_reqadd >= MAX_NEWHDR) { + Alert("parsing [%s:%d] : too many `reqadd'. Continuing.\n", file, linenum); continue; } @@ -3586,11 +3719,6 @@ int readcfgfile(char *file) { } else if (!strcmp(args[0], "srvexp") || !strcmp(args[0], "rsprep")) { /* replace response header from a regex */ regex_t *preg; - if (curproxy->nb_rspexp >= MAX_REGEXP) { - Alert("parsing [%s:%d] : too many server expressions. Continuing.\n", - file, linenum); - continue; - } if (*(args[1]) == 0 || *(args[2]) == 0) { Alert("parsing [%s:%d] : expects and as arguments.\n", @@ -3603,18 +3731,11 @@ int readcfgfile(char *file) { Alert("parsing [%s:%d] : bad regular expression <%s>.\n", file, linenum, args[1]); return -1; } - // fprintf(stderr,"before=<%s> after=<%s>\n", args[1], args[2]); - curproxy->rsp_exp[curproxy->nb_rspexp].preg = preg; - curproxy->rsp_exp[curproxy->nb_rspexp].replace = strdup(args[2]); - curproxy->nb_rspexp++; + + chain_regex(&curproxy->rsp_exp, preg, ACT_REPLACE, strdup(args[2])); } else if (!strcmp(args[0], "rspdel")) { /* delete response header from a regex */ regex_t *preg; - if (curproxy->nb_rspexp >= MAX_REGEXP) { - Alert("parsing [%s:%d] : too many server expressions. Continuing.\n", - file, linenum); - continue; - } if (*(args[1]) == 0) { Alert("parsing [%s:%d] : expects as an argument.\n", @@ -3627,14 +3748,12 @@ int readcfgfile(char *file) { Alert("parsing [%s:%d] : bad regular expression <%s>.\n", file, linenum, args[1]); return -1; } - // fprintf(stderr,"before=<%s> after=<%s>\n", args[1], args[2]); - curproxy->rsp_exp[curproxy->nb_rspexp].preg = preg; - curproxy->rsp_exp[curproxy->nb_rspexp].replace = NULL; /* means it must be deleted */ - curproxy->nb_rspexp++; + + chain_regex(&curproxy->rsp_exp, preg, ACT_REMOVE, strdup(args[2])); } else if (!strcmp(args[0], "rspadd")) { /* add response header */ - if (curproxy->nb_rspadd >= MAX_REGEXP) { - Alert("parsing [%s:%d] : too many server expressions. Continuing.\n", + if (curproxy->nb_rspadd >= MAX_NEWHDR) { + Alert("parsing [%s:%d] : too many `rspadd'. Continuing.\n", file, linenum); continue; } @@ -3701,11 +3820,11 @@ int readcfgfile(char *file) { Warning("parsing %s : servers will be ignored for listener %s.\n", file, curproxy->id); } - if (curproxy->nb_rspexp) { + if (curproxy->rsp_exp != NULL) { Warning("parsing %s : server regular expressions will be ignored for listener %s.\n", file, curproxy->id); } - if (curproxy->nb_reqexp) { + if (curproxy->req_exp != NULL) { Warning("parsing %s : client regular expressions will be ignored for listener %s.\n", file, curproxy->id); }