]> git.ipfire.org Git - thirdparty/haproxy.git/commitdiff
[MEDIUM] Add stick and store rules analysers.
authorEmeric Brun <ebrun@exceliance.fr>
Mon, 4 Jan 2010 14:47:17 +0000 (15:47 +0100)
committerWilly Tarreau <w@1wt.eu>
Tue, 12 Jan 2010 15:01:24 +0000 (16:01 +0100)
include/types/buffers.h
src/cfgparse.c
src/session.c

index ba7bcb8ae781a96fa6bde0133ba4c50bbda9fbe1..59e6d2960027fd140482c65297b6065b1c768409 100644 (file)
 #define AN_REQ_HTTP_INNER       0x00000020  /* inner processing of HTTP request */
 #define AN_REQ_HTTP_TARPIT      0x00000040  /* wait for end of HTTP tarpit */
 #define AN_REQ_HTTP_BODY        0x00000080  /* inspect HTTP request body */
-/* unused: 0x100, 0x200 */
+#define AN_REQ_STICKING_RULES   0x00000100  /* table persistence matching */
+/* unused: 0x200 */
 #define AN_REQ_PRST_RDP_COOKIE  0x00000400  /* persistence on rdp cookie */
 #define AN_REQ_HTTP_XFER_BODY   0x00000800  /* forward request body */
 
 #define AN_RES_WAIT_HTTP        0x00020000  /* wait for HTTP response */
 #define AN_RES_HTTP_PROCESS_BE  0x00040000  /* process backend's HTTP part */
 #define AN_RES_HTTP_PROCESS_FE  0x00040000  /* process frontend's HTTP part (same for now) */
+#define AN_RES_STORE_RULES      0x00080000  /* table persistence matching */
 #define AN_RES_HTTP_XFER_BODY   0x00100000  /* forward response body */
 
 
index d4ddc2ea4645f3ec1b0e68fa1766ac60b0b6cce7..39a79f2f7b49fd556e777557b4e57597d6de8472 100644 (file)
@@ -4621,6 +4621,10 @@ int check_config_validity()
                list_for_each_entry(mrule, &curproxy->sticking_rules, list) {
                        struct proxy *target;
 
+                       curproxy->be_req_ana |= AN_REQ_STICKING_RULES;
+                       if (mrule->flags & STK_IS_STORE)
+                               curproxy->be_rsp_ana |= AN_RES_STORE_RULES;
+
                        if (mrule->table.name)
                                target = findproxy(mrule->table.name, PR_CAP_BE);
                        else
@@ -4651,6 +4655,8 @@ int check_config_validity()
                list_for_each_entry(mrule, &curproxy->storersp_rules, list) {
                        struct proxy *target;
 
+                       curproxy->be_rsp_ana |= AN_RES_STORE_RULES;
+
                        if (mrule->table.name)
                                target = findproxy(mrule->table.name, PR_CAP_BE);
                        else
index 65e22f601d0c0c10caf1493903db38310a7458db..20a5886a8cf239ee8df753416ec4d55df1b5357a 100644 (file)
 #include <proto/hdr_idx.h>
 #include <proto/log.h>
 #include <proto/session.h>
+#include <proto/pattern.h>
 #include <proto/pipe.h>
 #include <proto/proto_http.h>
 #include <proto/proto_tcp.h>
 #include <proto/proxy.h>
 #include <proto/queue.h>
 #include <proto/server.h>
+#include <proto/stick_table.h>
 #include <proto/stream_interface.h>
 #include <proto/stream_sock.h>
 #include <proto/task.h>
@@ -610,6 +612,169 @@ int process_switching_rules(struct session *s, struct buffer *req, int an_bit)
        return 0;
 }
 
+/* This stream analyser works on a request. It applies all sticking rules on
+ * it then returns 1. The data must already be present in the buffer otherwise
+ * they won't match. It always returns 1.
+ */
+int process_sticking_rules(struct session *s, struct buffer *req, int an_bit)
+{
+       struct proxy    *px   = s->be;
+       struct sticking_rule  *rule;
+
+       DPRINTF(stderr,"[%u] %s: session=%p b=%p, exp(r,w)=%u,%u bf=%08x bl=%d analysers=%02x\n",
+               now_ms, __FUNCTION__,
+               s,
+               req,
+               req->rex, req->wex,
+               req->flags,
+               req->l,
+               req->analysers);
+
+       list_for_each_entry(rule, &px->sticking_rules, list) {
+               int ret = 1 ;
+               int i;
+
+               for (i = 0; i < s->store_count; i++) {
+                       if (rule->table.t == s->store[i].table)
+                               break;
+               }
+
+               if (i !=  s->store_count)
+                       continue;
+
+               if (rule->cond) {
+                       ret = acl_exec_cond(rule->cond, px, s, &s->txn, ACL_DIR_REQ);
+                       ret = acl_pass(ret);
+                       if (rule->cond->pol == ACL_COND_UNLESS)
+                               ret = !ret;
+               }
+
+               if (ret) {
+                       struct stktable_key *key;
+
+                       key = pattern_process_key(px, s, &s->txn, PATTERN_FETCH_REQ, rule->expr, rule->table.t->type);
+                       if (!key)
+                               continue;
+
+                       if (rule->flags & STK_IS_MATCH) {
+                               struct stksess *ts;
+
+                               if ((ts = stktable_lookup(rule->table.t, key)) != NULL) {
+                                       if (!(s->flags & SN_ASSIGNED)) {
+                                               struct eb32_node *node;
+
+                                               /* srv found in table */
+                                               node = eb32_lookup(&px->conf.used_server_id, ts->sid);
+                                               if (node) {
+                                                       struct server *srv;
+
+                                                       srv = container_of(node, struct server, conf.id);
+                                                       if ((srv->state & SRV_RUNNING) || (px->options & PR_O_PERSIST)) {
+                                                               s->flags |= SN_DIRECT | SN_ASSIGNED;
+                                                               s->srv = srv;
+                                                       }
+                                               }
+                                       }
+                                       ts->expire = tick_add(now_ms, MS_TO_TICKS(rule->table.t->expire));
+                               }
+                       }
+                       if (rule->flags & STK_IS_STORE) {
+                               if (s->store_count < (sizeof(s->store) / sizeof(s->store[0]))) {
+                                       struct stksess *ts;
+
+                                       ts = stksess_new(rule->table.t, key);
+                                       if (ts) {
+                                               s->store[s->store_count].table = rule->table.t;
+                                               s->store[s->store_count++].ts = ts;
+                                       }
+                               }
+                       }
+               }
+       }
+
+       req->analysers &= ~an_bit;
+       req->analyse_exp = TICK_ETERNITY;
+       return 1;
+}
+
+/* This stream analyser works on a response. It applies all store rules on it
+ * then returns 1. The data must already be present in the buffer otherwise
+ * they won't match. It always returns 1.
+ */
+int process_store_rules(struct session *s, struct buffer *rep, int an_bit)
+{
+       struct proxy    *px   = s->be;
+       struct sticking_rule  *rule;
+       int i;
+
+       DPRINTF(stderr,"[%u] %s: session=%p b=%p, exp(r,w)=%u,%u bf=%08x bl=%d analysers=%02x\n",
+               now_ms, __FUNCTION__,
+               s,
+               req,
+               req->rex, req->wex,
+               req->flags,
+               req->l,
+               req->analysers);
+
+       list_for_each_entry(rule, &px->storersp_rules, list) {
+               int ret = 1 ;
+               int storereqidx = -1;
+
+               for (i = 0; i < s->store_count; i++) {
+                       if (rule->table.t == s->store[i].table) {
+                               if (!(s->store[i].flags))
+                                       storereqidx = i;
+                               break;
+                       }
+               }
+
+               if ((i !=  s->store_count) && (storereqidx == -1))
+                       continue;
+
+               if (rule->cond) {
+                       ret = acl_exec_cond(rule->cond, px, s, &s->txn, ACL_DIR_RTR);
+                       ret = acl_pass(ret);
+                       if (rule->cond->pol == ACL_COND_UNLESS)
+                               ret = !ret;
+               }
+
+               if (ret) {
+                       struct stktable_key *key;
+
+                       key = pattern_process_key(px, s, &s->txn, PATTERN_FETCH_RTR, rule->expr, rule->table.t->type);
+                       if (!key)
+                               continue;
+
+                       if (storereqidx != -1) {
+                               stksess_key(s->store[storereqidx].table, s->store[storereqidx].ts, key);
+                               s->store[storereqidx].flags = 1;
+                       }
+                       else if (s->store_count < (sizeof(s->store) / sizeof(s->store[0]))) {
+                               struct stksess *ts;
+
+                               ts = stksess_new(rule->table.t, key);
+                               if (ts) {
+                                       s->store[s->store_count].table = rule->table.t;
+                                       s->store[s->store_count].flags = 1;
+                                       s->store[s->store_count++].ts = ts;
+                               }
+                       }
+               }
+       }
+
+       /* process store request and store response */
+       for (i = 0; i < s->store_count; i++) {
+               if (stktable_store(s->store[i].table, s->store[i].ts, s->srv->puid) > 0) {
+                       stksess_free(s->store[i].table, s->store[i].ts);
+                       s->store[i].ts = NULL;
+               }
+       }
+
+       rep->analysers &= ~an_bit;
+       rep->analyse_exp = TICK_ETERNITY;
+       return 1;
+}
+
 /* This macro is very specific to the function below. See the comments in
  * process_session() below to understand the logic and the tests.
  */
@@ -893,6 +1058,12 @@ resync_stream_interface:
                                        UPDATE_ANALYSERS(s->req->analysers, ana_list, ana_back, AN_REQ_PRST_RDP_COOKIE);
                                }
 
+                               if (ana_list & AN_REQ_STICKING_RULES) {
+                                       if (!process_sticking_rules(s, s->req, AN_REQ_STICKING_RULES))
+                                               break;
+                                       UPDATE_ANALYSERS(s->req->analysers, ana_list, ana_back, AN_REQ_STICKING_RULES);
+                               }
+
                                if (ana_list & AN_REQ_HTTP_XFER_BODY) {
                                        if (!http_request_forward_body(s, s->req, AN_REQ_HTTP_XFER_BODY))
                                                break;
@@ -975,6 +1146,12 @@ resync_stream_interface:
                                        UPDATE_ANALYSERS(s->rep->analysers, ana_list, ana_back, AN_RES_WAIT_HTTP);
                                }
 
+                               if (ana_list & AN_RES_STORE_RULES) {
+                                       if (!process_store_rules(s, s->rep, AN_RES_STORE_RULES))
+                                               break;
+                                       UPDATE_ANALYSERS(s->rep->analysers, ana_list, ana_back, AN_RES_STORE_RULES);
+                               }
+
                                if (ana_list & AN_RES_HTTP_PROCESS_BE) {
                                        if (!http_process_res_common(s, s->rep, AN_RES_HTTP_PROCESS_BE, s->be))
                                                break;