]> git.ipfire.org Git - thirdparty/haproxy.git/commitdiff
[MEDIUM] Add stick table configuration and init.
authorEmeric Brun <ebrun@exceliance.fr>
Mon, 4 Jan 2010 14:45:53 +0000 (15:45 +0100)
committerWilly Tarreau <w@1wt.eu>
Tue, 12 Jan 2010 15:01:24 +0000 (16:01 +0100)
include/types/proxy.h
include/types/session.h
src/cfgparse.c
src/client.c
src/proto_http.c
src/proto_uxst.c

index a7887e6ae47b9ed0e12b3650d5e84744ec748bfa..a4c99cbeb11d614ae8defd15f3e8d96ef6a2a023 100644 (file)
@@ -36,6 +36,7 @@
 #include <eb32tree.h>
 
 #include <types/acl.h>
+#include <types/pattern.h>
 #include <types/backend.h>
 #include <types/buffers.h>
 #include <types/counters.h>
@@ -45,6 +46,7 @@
 #include <types/protocols.h>
 #include <types/session.h>
 #include <types/server.h>
+#include <types/stick_table.h>
 
 /* values for proxy->state */
 #define PR_STNEW        0
 #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 */
index a8ec1e391aed1ccf81d24fdd4f7a2fd732c75fdd..ecbb6c1ffc3fb83dbdb99ac327699cdc06124428 100644 (file)
@@ -38,6 +38,7 @@
 #include <types/server.h>
 #include <types/stream_interface.h>
 #include <types/task.h>
+#include <types/stick_table.h>
 
 
 /* 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 */
index 7a7af790c733f2375d747895182bac648aeb7f1c..d4ddc2ea4645f3ec1b0e68fa1766ac60b0b6cce7 100644 (file)
@@ -47,6 +47,7 @@
 #include <proto/lb_fwrr.h>
 #include <proto/lb_map.h>
 #include <proto/log.h>
+#include <proto/pattern.h>
 #include <proto/port_range.h>
 #include <proto/protocols.h>
 #include <proto/proto_tcp.h>
@@ -55,6 +56,7 @@
 #include <proto/server.h>
 #include <proto/session.h>
 #include <proto/task.h>
+#include <proto/stick_table.h>
 
 
 /* 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.
                         */
index ab8e92ccfc7d12dc1e51e8270b357aa96df79b6b..16ee2d09f0580ab0c5165fa0dbc6615827614a3a 100644 (file)
@@ -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 <p>, <p>, and later <be> and <be>.
                 */
index d62ac73fa17db08bc871d3995e8f7041248b8251..91182935e6639faf699c5a3c8e0446dac8c8b54a 100644 (file)
@@ -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;
 
index 186ddda54c68e4f178c75e9be61c107770e77ce0..7d38569bbfd767971ae3b576fae77cbc3fa3bbd6 100644 (file)
@@ -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));