From: Willy Tarreau Date: Sun, 17 Dec 2006 22:15:24 +0000 (+0100) Subject: [MAJOR] merged the 'setbe' actions to switch the backend on a regex X-Git-Tag: v1.3.4~22 X-Git-Url: http://git.ipfire.org/?a=commitdiff_plain;h=a496b6042b52a6500c19fdf4ce679295bea29c3b;p=thirdparty%2Fhaproxy.git [MAJOR] merged the 'setbe' actions to switch the backend on a regex Sin Yu's patch to permit to change the proxy from a regex was merged with little changes : - req_cap/rsp_cap are not reassigned to the new proxy, they stay attached to the frontend - the actions have been renamed "reqsetbe" and "reqisetbe" for "set BackEnd". - the buffer is not reset after the switch, instead, the headers are parsed again by the backend - in Sin's patch, it was theorically possible to switch multiple times, but the switching track was lost, making it impossible to apply server responsesin the reverse order. Now switching is limited to 1 action (separation between frontend and backend) but the filters remain. Now it will be extremely easy to add other switching conditions, such as host matching, URI matching, etc... There's still a hard work to be done on the logs and stats. --- diff --git a/include/common/regex.h b/include/common/regex.h index bf51a0dcd7..bc86bbdf31 100644 --- a/include/common/regex.h +++ b/include/common/regex.h @@ -38,6 +38,7 @@ #define ACT_DENY 3 /* deny the request */ #define ACT_PASS 4 /* pass this header without allowing or denying the request */ #define ACT_TARPIT 5 /* tarpit the connection matching this request */ +#define ACT_SETBE 6 /* switch the backend */ struct hdr_exp { struct hdr_exp *next; diff --git a/src/cfgparse.c b/src/cfgparse.c index b3747c4e6c..0034bdab0b 100644 --- a/src/cfgparse.c +++ b/src/cfgparse.c @@ -1416,6 +1416,46 @@ int cfg_parse_listen(const char *file, int linenum, char **args) chain_regex(&curproxy->req_exp, preg, ACT_TARPIT, NULL); } + else if (!strcmp(args[0], "reqsetbe")) { /* switch the backend from a regex, respecting case */ + regex_t *preg; + if(curproxy == &defproxy) { + Alert("parsing [%s:%d] : '%s' not allowed in 'defaults' section.\n", file, linenum, args[0]); + return -1; + } + + if(*(args[1]) == 0 || *(args[2]) == 0) { + Alert("parsing [%s:%d] : '%s' expects and as arguments.\n", + file, linenum, args[0]); + 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]); + } + + chain_regex(&curproxy->req_exp, preg, ACT_SETBE, strdup(args[2])); + } + else if (!strcmp(args[0], "reqisetbe")) { /* switch the backend from a regex, ignoring case */ + regex_t *preg; + if(curproxy == &defproxy) { + Alert("parsing [%s:%d] : '%s' not allowed in 'defaults' section.\n", file, linenum, args[0]); + return -1; + } + + if(*(args[1]) == 0 || *(args[2]) == 0) { + Alert("parsing [%s:%d] : '%s' expects and as arguments.\n", + file, linenum, args[0]); + return -1; + } + + preg = calloc(1, sizeof(regex_t)); + if(regcomp(preg, args[1], REG_EXTENDED | REG_ICASE) != 0) { + Alert("parsing [%s:%d] : bad regular expression '%s'.\n", file, linenum, args[1]); + } + + chain_regex(&curproxy->req_exp, preg, ACT_SETBE, strdup(args[2])); + } else if (!strcmp(args[0], "reqirep")) { /* replace request header from a regex, ignoring case */ regex_t *preg; if (curproxy == &defproxy) { @@ -1945,8 +1985,6 @@ int readcfgfile(const char *file) Alert("parsing [%s:%d] : unknown keyword '%s' out of section.\n", file, linenum, args[0]); return -1; } - - } fclose(f); @@ -2030,6 +2068,30 @@ int readcfgfile(const char *file) } } + if (curproxy->mode == PR_MODE_HTTP && curproxy->req_exp != NULL) { + /* map jump target for ACT_SETBE in req_rep chain */ + struct hdr_exp *exp; + struct proxy *target; + for (exp = curproxy->req_exp; exp != NULL; exp = exp->next) { + if (exp->action != ACT_SETBE) + continue; + for (target = proxy; target != NULL; target = target->next) { + if (strcmp(target->id, exp->replace) == 0) + break; + } + if (target == NULL) { + Alert("parsing %s : backend '%s' in HTTP proxy %s was not found !\n", + file, exp->replace, curproxy->id); + cfgerr++; + } else if (target == curproxy) { + Alert("parsing %s : loop detected for backend %s !\n", file, exp->replace); + cfgerr++; + } else { + free((void *)exp->replace); + exp->replace = (const char *)target; + } + } + } if ((curproxy->mode == PR_MODE_TCP || curproxy->mode == PR_MODE_HTTP) && (!curproxy->clitimeout || !curproxy->contimeout || !curproxy->srvtimeout)) { Warning("parsing %s : missing timeouts for listener '%s'.\n" diff --git a/src/proto_http.c b/src/proto_http.c index 8ee2cf8641..9cce59ba76 100644 --- a/src/proto_http.c +++ b/src/proto_http.c @@ -3041,6 +3041,36 @@ void apply_filters_to_session(struct session *t, struct buffer *req, struct hdr_ if (regexec(exp->preg, cur_ptr, MAX_MATCH, pmatch, 0) == 0) { switch (exp->action) { + case ACT_SETBE: + /* It is not possible to jump a second time. + * FIXME: should we return an HTTP/500 here so that + * the admin knows there's a problem ? + */ + if (t->be != t->fe) + break; + + if (!(t->flags & (SN_CLDENY | SN_CLTARPIT))) { + struct proxy *target = (struct proxy *) exp->replace; + + /* Swithing Proxy */ + *cur_end = term; + cur_end = NULL; + + /* right now, the backend switch is not too much complicated + * because we have associated req_cap and rsp_cap to the + * frontend, and the beconn will be updated later. + */ + + t->rep->rto = t->req->wto = target->beprm->srvtimeout; + t->req->cto = target->beprm->contimeout; + + t->be = target; + + //t->logs.logwait |= LW_REQ | (target->to_log & (LW_REQHDR | LW_COOKIE)); + t->logs.logwait |= (target->to_log | target->beprm->to_log); + abort_filt = 1; + } + break; case ACT_ALLOW: if (!(t->flags & (SN_CLDENY | SN_CLTARPIT))) { t->flags |= SN_CLALLOW;