From: Emeric Brun Date: Mon, 4 Jan 2010 14:45:53 +0000 (+0100) Subject: [MEDIUM] Add stick table configuration and init. X-Git-Tag: v1.4-dev7~23 X-Git-Url: http://git.ipfire.org/cgi-bin/gitweb.cgi?a=commitdiff_plain;h=b982a3d23a784dbfb1e151c4fc70599bfc4bf67d;p=thirdparty%2Fhaproxy.git [MEDIUM] Add stick table configuration and init. --- diff --git a/include/types/proxy.h b/include/types/proxy.h index a7887e6ae4..a4c99cbeb1 100644 --- a/include/types/proxy.h +++ b/include/types/proxy.h @@ -36,6 +36,7 @@ #include #include +#include #include #include #include @@ -45,6 +46,7 @@ #include #include #include +#include /* values for proxy->state */ #define PR_STNEW 0 @@ -133,6 +135,12 @@ #define PR_O2_AS_M_ANY 0x00010000 /* mask covering all PR_O2_AS_M_* values */ #define PR_O2_MYSQL_CHK 0x00020000 /* use MYSQL check for server health */ +/* end of proxy->options2 */ + +/* bits for sticking rules */ +#define STK_IS_MATCH 0x00000001 /* match on request fetch */ +#define STK_IS_STORE 0x00000002 /* store on request fetch */ +#define STK_ON_RSP 0x00000004 /* store on response fetch */ struct error_snapshot { struct timeval when; /* date of this event, (tv_sec == 0) means "never" */ @@ -163,6 +171,8 @@ struct proxy { struct list block_cond; /* early blocking conditions (chained) */ struct list redirect_rules; /* content redirecting rules (chained) */ struct list switching_rules; /* content switching rules (chained) */ + struct list sticking_rules; /* content sticking rules (chained) */ + struct list storersp_rules; /* content store response rules (chained) */ struct { /* TCP request processing */ unsigned int inspect_delay; /* inspection delay */ struct list inspect_rules; /* inspection rules */ @@ -253,6 +263,9 @@ struct proxy { struct pool_head *hdr_idx_pool; /* pools of pre-allocated int* used for headers indexing */ struct list req_add, rsp_add; /* headers to be added */ struct pxcounters counters; /* statistics counters */ + + struct stktable table; /* table for storing sticking sessions */ + int grace; /* grace time after stop request */ char *check_req; /* HTTP or SSL request to use for PR_O_HTTP_CHK|PR_O_SSL3_CHK */ int check_len; /* Length of the HTTP or SSL3 request */ @@ -284,6 +297,18 @@ struct switching_rule { } be; }; +struct sticking_rule { + struct list list; /* list linked to from the proxy */ + struct acl_cond *cond; /* acl condition to meet */ + struct pattern_expr *expr; /* fetch expr to fetch key */ + int flags; /* STK_* */ + union { + struct stktable *t; /* target table */ + char *name; /* target table name during config parsing */ + } table; +}; + + struct redirect_rule { struct list list; /* list linked to from the proxy */ struct acl_cond *cond; /* acl condition to meet */ diff --git a/include/types/session.h b/include/types/session.h index a8ec1e391a..ecbb6c1ffc 100644 --- a/include/types/session.h +++ b/include/types/session.h @@ -38,6 +38,7 @@ #include #include #include +#include /* various session flags, bits values 0x01 to 0x100 (shift 0) */ @@ -174,6 +175,14 @@ struct session { struct server *prev_srv; /* the server the was running on, after a redispatch, otherwise NULL */ struct pendconn *pend_pos; /* if not NULL, points to the position in the pending queue */ struct http_txn txn; /* current HTTP transaction being processed. Should become a list. */ + + struct { + struct stksess *ts; + struct stktable *table; + int flags; + } store[8]; /* tracked stickiness values to store */ + int store_count; + struct { int logwait; /* log fields waiting to be collected : LW_* */ struct timeval accept_date; /* date of the accept() in user date */ diff --git a/src/cfgparse.c b/src/cfgparse.c index 7a7af790c7..d4ddc2ea46 100644 --- a/src/cfgparse.c +++ b/src/cfgparse.c @@ -47,6 +47,7 @@ #include #include #include +#include #include #include #include @@ -55,6 +56,7 @@ #include #include #include +#include /* This is the SSLv3 CLIENT HELLO packet used in conjunction with the @@ -795,6 +797,8 @@ static void init_new_proxy(struct proxy *p) LIST_INIT(&p->redirect_rules); LIST_INIT(&p->mon_fail_cond); LIST_INIT(&p->switching_rules); + LIST_INIT(&p->sticking_rules); + LIST_INIT(&p->storersp_rules); LIST_INIT(&p->tcp_req.inspect_rules); LIST_INIT(&p->req_add); LIST_INIT(&p->rsp_add); @@ -1541,7 +1545,7 @@ int cfg_parse_listen(const char *file, int linenum, char **args, int kwm) if (!strncmp(args[1], "rdp-cookie", 10)) { curproxy->options2 |= PR_O2_RDPC_PRST; - if (*(args[1] + 10 ) == '(') { /* cookie name */ + if (*(args[1] + 10) == '(') { /* cookie name */ const char *beg, *end; beg = args[1] + 11; @@ -1558,7 +1562,7 @@ int cfg_parse_listen(const char *file, int linenum, char **args, int kwm) curproxy->rdp_cookie_name = my_strndup(beg, end - beg); curproxy->rdp_cookie_len = end-beg; } - else if (*(args[1] + 10 ) == '\0') { /* default cookie name 'msts' */ + else if (*(args[1] + 10) == '\0') { /* default cookie name 'msts' */ free(curproxy->rdp_cookie_name); curproxy->rdp_cookie_name = strdup("msts"); curproxy->rdp_cookie_len = strlen(curproxy->rdp_cookie_name); @@ -1982,6 +1986,199 @@ int cfg_parse_listen(const char *file, int linenum, char **args, int kwm) LIST_INIT(&rule->list); LIST_ADDQ(&curproxy->switching_rules, &rule->list); } + else if (!strcmp(args[0], "stick-table")) { + int myidx = 1; + + curproxy->table.type = (unsigned int)-1; + while (*args[myidx]) { + const char *err; + + if (strcmp(args[myidx], "size") == 0) { + myidx++; + if (!*(args[myidx])) { + Alert("parsing [%s:%d] : stick-table: missing argument after '%s'.\n", + file, linenum, args[myidx-1]); + err_code |= ERR_ALERT | ERR_FATAL; + goto out; + } + if ((err = parse_size_err(args[myidx], &curproxy->table.size))) { + Alert("parsing [%s:%d] : stick-table: unexpected character '%c' in argument of '%s'.\n", + file, linenum, *err, args[myidx-1]); + err_code |= ERR_ALERT | ERR_FATAL; + goto out; + } + } + else if (strcmp(args[myidx], "expire") == 0) { + myidx++; + if (!*(args[myidx])) { + Alert("parsing [%s:%d] : stick-table: missing argument after '%s'.\n", + file, linenum, args[myidx-1]); + err_code |= ERR_ALERT | ERR_FATAL; + goto out; + } + err = parse_time_err(args[myidx], &val, TIME_UNIT_MS); + if (err) { + Alert("parsing [%s:%d] : stick-table: unexpected character '%c' in argument of '%s'.\n", + file, linenum, *err, args[myidx-1]); + err_code |= ERR_ALERT | ERR_FATAL; + goto out; + } + curproxy->table.expire = val; + } + else if (strcmp(args[myidx], "nopurge") == 0) { + curproxy->table.nopurge = 1; + } + else if (strcmp(args[myidx], "type") == 0) { + myidx++; + if (stktable_parse_type(args, &myidx, &curproxy->table.type, &curproxy->table.key_size) != 0) { + Alert("parsing [%s:%d] : stick-table: unknown type '%s'.\n", + file, linenum, args[myidx]); + err_code |= ERR_ALERT | ERR_FATAL; + goto out; + } + } + myidx++; + } + + if (!curproxy->table.size) { + Alert("parsing [%s:%d] : stick-table: missing size.\n", + file, linenum); + err_code |= ERR_ALERT | ERR_FATAL; + goto out; + } + + if (curproxy->table.type == (unsigned int)-1) { + Alert("parsing [%s:%d] : stick-table: missing type.\n", + file, linenum); + err_code |= ERR_ALERT | ERR_FATAL; + goto out; + } + } + else if (!strcmp(args[0], "stick")) { + int pol = ACL_COND_NONE; + struct acl_cond *cond = NULL; + struct sticking_rule *rule; + struct pattern_expr *expr; + int myidx = 0; + const char *name = NULL; + int flags; + + if (curproxy == &defproxy) { + Alert("parsing [%s:%d] : '%s' not allowed in 'defaults' section.\n", file, linenum, args[0]); + err_code |= ERR_ALERT | ERR_FATAL; + goto out; + } + + if (warnifnotcap(curproxy, PR_CAP_BE, file, linenum, args[0], NULL)) { + err_code |= ERR_WARN; + goto out; + } + + myidx++; + if ((strcmp(args[myidx], "store") == 0) || + (strcmp(args[myidx], "store-request") == 0)) { + myidx++; + flags = STK_IS_STORE; + } + else if (strcmp(args[myidx], "store-response") == 0) { + myidx++; + flags = STK_IS_STORE | STK_ON_RSP; + } + else if (strcmp(args[myidx], "match") == 0) { + myidx++; + flags = STK_IS_MATCH; + } + else if (strcmp(args[myidx], "on") == 0) { + myidx++; + flags = STK_IS_MATCH | STK_IS_STORE; + } + else { + Alert("parsing [%s:%d] : '%s' expects 'on', 'match', or 'store'.\n", file, linenum, args[0]); + err_code |= ERR_ALERT | ERR_FATAL; + goto out; + } + + if (*(args[myidx]) == 0) { + Alert("parsing [%s:%d] : '%s' expects a fetch method.\n", file, linenum, args[0]); + err_code |= ERR_ALERT | ERR_FATAL; + goto out; + } + + expr = pattern_parse_expr(args, &myidx); + if (!expr) { + Alert("parsing [%s:%d] : '%s': unknown fetch method '%s'.\n", file, linenum, args[0], args[myidx]); + err_code |= ERR_ALERT | ERR_FATAL; + goto out; + } + + if (flags & STK_ON_RSP) { + if (!(expr->fetch->dir & PATTERN_FETCH_RTR)) { + Alert("parsing [%s:%d] : '%s': fetch method '%s' can not be used on response.\n", + file, linenum, args[0], expr->fetch->kw); + err_code |= ERR_ALERT | ERR_FATAL; + goto out; + } + } else { + if (!(expr->fetch->dir & PATTERN_FETCH_REQ)) { + Alert("parsing [%s:%d] : '%s': fetch method '%s' can not be used on request.\n", + file, linenum, args[0], expr->fetch->kw); + err_code |= ERR_ALERT | ERR_FATAL; + goto out; + } + } + + if (strcmp(args[myidx], "table") == 0) { + myidx++; + name = args[myidx++]; + } + + if (*(args[myidx]) == 0) + pol = ACL_COND_NONE; + else if (strcmp(args[myidx], "if") == 0) + pol = ACL_COND_IF; + else if (strcmp(args[myidx], "unless") == 0) + pol = ACL_COND_UNLESS; + else { + Alert("parsing [%s:%d] : '%s': unknown keyword '%s'.\n", + file, linenum, args[0], args[myidx]); + err_code |= ERR_ALERT | ERR_FATAL; + goto out; + } + + if (pol != ACL_COND_NONE) { + myidx++; + if ((cond = parse_acl_cond((const char **)args + myidx, &curproxy->acl, pol)) == NULL) { + Alert("parsing [%s:%d] : '%s': error detected while parsing sticking condition.\n", + file, linenum, args[0]); + err_code |= ERR_ALERT | ERR_FATAL; + goto out; + } + + cond->file = file; + cond->line = linenum; + curproxy->acl_requires |= cond->requires; + if (cond->requires & ACL_USE_RTR_ANY) { + struct acl *acl; + const char *name; + + acl = cond_find_require(cond, ACL_USE_RTR_ANY); + name = acl ? acl->name : "(unknown)"; + Warning("parsing [%s:%d] : '%s' : acl '%s' involves some response-only criteria which will be ignored.\n", + file, linenum, args[0], name); + err_code |= ERR_WARN; + } + } + rule = (struct sticking_rule *)calloc(1, sizeof(*rule)); + rule->cond = cond; + rule->expr = expr; + rule->flags = flags; + rule->table.name = name ? strdup(name) : NULL; + LIST_INIT(&rule->list); + if (flags & STK_ON_RSP) + LIST_ADDQ(&curproxy->storersp_rules, &rule->list); + else + LIST_ADDQ(&curproxy->sticking_rules, &rule->list); + } else if (!strcmp(args[0], "stats")) { if (warnifnotcap(curproxy, PR_CAP_BE, file, linenum, args[0], NULL)) err_code |= ERR_WARN; @@ -4085,7 +4282,7 @@ int readcfgfile(const char *file) skip = 1; } else if (line[1] == 'x') { - if ((line + 3 < end ) && ishex(line[2]) && ishex(line[3])) { + if ((line + 3 < end) && ishex(line[2]) && ishex(line[3])) { unsigned char hex1, hex2; hex1 = toupper(line[2]) - '0'; hex2 = toupper(line[3]) - '0'; @@ -4247,6 +4444,7 @@ int check_config_validity() while (curproxy != NULL) { struct switching_rule *rule; + struct sticking_rule *mrule; struct listener *listener; unsigned int next_id; @@ -4419,6 +4617,66 @@ int check_config_validity() } } + /* find the target table for 'stick' rules */ + list_for_each_entry(mrule, &curproxy->sticking_rules, list) { + struct proxy *target; + + if (mrule->table.name) + target = findproxy(mrule->table.name, PR_CAP_BE); + else + target = curproxy; + + if (!target) { + Alert("Proxy '%s': unable to find stick-table '%s'.\n", + curproxy->id, mrule->table.name); + cfgerr++; + } + else if (target->table.size == 0) { + Alert("Proxy '%s': stick-table '%s' used but not configured.\n", + curproxy->id, mrule->table.name ? mrule->table.name : curproxy->id); + cfgerr++; + } + else if (pattern_notusable_key(mrule->expr, target->table.type)) { + Alert("Proxy '%s': type of pattern not usable with type of stick-table '%s'.\n", + curproxy->id, mrule->table.name ? mrule->table.name : curproxy->id); + cfgerr++; + } + else { + free((void *)mrule->table.name); + mrule->table.t = &(target->table); + } + } + + /* find the target table for 'store response' rules */ + list_for_each_entry(mrule, &curproxy->storersp_rules, list) { + struct proxy *target; + + if (mrule->table.name) + target = findproxy(mrule->table.name, PR_CAP_BE); + else + target = curproxy; + + if (!target) { + Alert("Proxy '%s': unable to find store table '%s'.\n", + curproxy->id, mrule->table.name); + cfgerr++; + } + else if (target->table.size == 0) { + Alert("Proxy '%s': stick-table '%s' used but not configured.\n", + curproxy->id, mrule->table.name ? mrule->table.name : curproxy->id); + cfgerr++; + } + else if (pattern_notusable_key(mrule->expr, target->table.type)) { + Alert("Proxy '%s': type of pattern not usable with type of stick-table '%s'.\n", + curproxy->id, mrule->table.name ? mrule->table.name : curproxy->id); + cfgerr++; + } + else { + free((void *)mrule->table.name); + mrule->table.t = &(target->table); + } + } + if ((curproxy->mode == PR_MODE_TCP || curproxy->mode == PR_MODE_HTTP) && (((curproxy->cap & PR_CAP_FE) && !curproxy->timeout.client) || ((curproxy->cap & PR_CAP_BE) && (curproxy->srv) && @@ -4679,6 +4937,9 @@ int check_config_validity() curproxy->be_rsp_ana |= AN_RES_WAIT_HTTP | AN_RES_HTTP_PROCESS_BE; } + /* init table on backend capabilities proxy */ + stktable_init(&curproxy->table); + /* If the backend does requires RDP cookie persistence, we have to * enable the corresponding analyser. */ diff --git a/src/client.c b/src/client.c index ab8e92ccfc..16ee2d09f0 100644 --- a/src/client.c +++ b/src/client.c @@ -225,6 +225,9 @@ int event_accept(int fd) { s->pend_pos = NULL; s->conn_retries = s->be->conn_retries; + /* init store persistence */ + s->store_count = 0; + /* FIXME: the logs are horribly complicated now, because they are * defined in

,

, and later and . */ diff --git a/src/proto_http.c b/src/proto_http.c index d62ac73fa1..91182935e6 100644 --- a/src/proto_http.c +++ b/src/proto_http.c @@ -6415,6 +6415,9 @@ void http_reset_txn(struct session *s) s->req->analysers = s->listener->analysers; s->logs.logwait = s->fe->to_log; s->srv = s->prev_srv = s->srv_conn = NULL; + /* re-init store persistence */ + s->store_count = 0; + s->pend_pos = NULL; s->conn_retries = s->be->conn_retries; diff --git a/src/proto_uxst.c b/src/proto_uxst.c index 186ddda54c..7d38569bbf 100644 --- a/src/proto_uxst.c +++ b/src/proto_uxst.c @@ -476,6 +476,8 @@ int uxst_event_accept(int fd) { s->srv = s->prev_srv = s->srv_conn = NULL; s->pend_pos = NULL; + s->store_count = 0; + memset(&s->logs, 0, sizeof(s->logs)); memset(&s->txn, 0, sizeof(s->txn));