with large lists! It is the equivalent of the "set map" command from the
stats socket, but can be triggered by an HTTP request.
+ - { track-sc0 | track-sc1 | track-sc2 } <key> [table <table>] :
+ enables tracking of sticky counters from current request. These rules
+ do not stop evaluation and do not change default action. Three sets of
+ counters may be simultaneously tracked by the same connection. The first
+ "track-sc0" rule executed enables tracking of the counters of the
+ specified table as the first set. The first "track-sc1" rule executed
+ enables tracking of the counters of the specified table as the second
+ set. The first "track-sc2" rule executed enables tracking of the
+ counters of the specified table as the third set. It is a recommended
+ practice to use the first set of counters for the per-frontend counters
+ and the second set for the per-backend ones. But this is just a
+ guideline, all may be used everywhere.
+
+ These actions take one or two arguments :
+ <key> is mandatory, and is a sample expression rule as described
+ in section 7.3. It describes what elements of the incoming
+ request or connection will be analysed, extracted, combined,
+ and used to select which table entry to update the counters.
+
+ <table> is an optional table to be used instead of the default one,
+ which is the stick-table declared in the current proxy. All
+ the counters for the matches and updates for the key will
+ then be performed in that table until the session ends.
+
+ Once a "track-sc*" rule is executed, the key is looked up in the table
+ and if it is not found, an entry is allocated for it. Then a pointer to
+ that entry is kept during all the session's life, and this entry's
+ counters are updated as often as possible, every time the session's
+ counters are updated, and also systematically when the session ends.
+ Counters are only updated for events that happen after the tracking has
+ been started. As an exception, connection counters and request counters
+ are systematically updated so that they reflect useful information.
+
+ If the entry tracks concurrent connection counters, one connection is
+ counted for as long as the entry is tracked, and the entry will not
+ expire during that time. Tracking counters also provides a performance
+ advantage over just checking the keys, because only one table lookup is
+ performed for all ACL checks that make use of it.
+
There is no limit to the number of http-request statements per instance.
It is important to know that http-request rules are processed very early in
- { track-sc0 | track-sc1 | track-sc2 } <key> [table <table>] :
enables tracking of sticky counters from current connection. These
- rules do not stop evaluation and do not change default action. Two sets
+ rules do not stop evaluation and do not change default action. 3 sets
of counters may be simultaneously tracked by the same connection. The
first "track-sc0" rule executed enables tracking of the counters of the
specified table as the first set. The first "track-sc1" rule executed
return len;
}
+/* for an http-request action HTTP_REQ_ACT_TRK_*, return a tracking index
+ * starting at zero for SC0. Unknown actions also return zero.
+ */
+static inline int http_req_trk_idx(int trk_action)
+{
+ return trk_action - HTTP_REQ_ACT_TRK_SC0;
+}
+
/* for debugging, reports the HTTP message state name */
static inline const char *http_msg_state_str(int msg_state)
{
#include <common/regex.h>
#include <types/hdr_idx.h>
+#include <types/stick_table.h>
/* These are the flags that are found in txn->flags */
HTTP_REQ_ACT_SET_MAP,
HTTP_REQ_ACT_CUSTOM_STOP,
HTTP_REQ_ACT_CUSTOM_CONT,
+ HTTP_REQ_ACT_TRK_SC0,
+ /* SC1, SC2, ... SCn */
+ HTTP_REQ_ACT_TRK_SCMAX = HTTP_REQ_ACT_TRK_SC0 + MAX_SESS_STKCTR - 1,
HTTP_REQ_ACT_MAX /* must always be last */
};
struct list value; /* pattern to retrieve MAP value */
} map;
} arg; /* arguments used by some actions */
+
+ union {
+ struct track_ctr_prm trk_ctr;
+ } act_prm;
};
struct http_res_rule {
struct server_rule *srule;
struct sticking_rule *mrule;
struct tcp_rule *trule;
+ struct http_req_rule *hrqrule;
struct listener *listener;
unsigned int next_id;
int nbproc;
}
}
+ /* find the target table for 'http-request' layer 7 rules */
+ list_for_each_entry(hrqrule, &curproxy->http_req_rules, list) {
+ struct proxy *target;
+
+ if (hrqrule->action < HTTP_REQ_ACT_TRK_SC0 || hrqrule->action > HTTP_REQ_ACT_TRK_SCMAX)
+ continue;
+
+ if (hrqrule->act_prm.trk_ctr.table.n)
+ target = findproxy(hrqrule->act_prm.trk_ctr.table.n, 0);
+ else
+ target = curproxy;
+
+ if (!target) {
+ Alert("Proxy '%s': unable to find table '%s' referenced by track-sc%d.\n",
+ curproxy->id, hrqrule->act_prm.trk_ctr.table.n,
+ http_req_trk_idx(hrqrule->action));
+ cfgerr++;
+ }
+ else if (target->table.size == 0) {
+ Alert("Proxy '%s': table '%s' used but not configured.\n",
+ curproxy->id, hrqrule->act_prm.trk_ctr.table.n ? hrqrule->act_prm.trk_ctr.table.n : curproxy->id);
+ cfgerr++;
+ }
+ else if (!stktable_compatible_sample(hrqrule->act_prm.trk_ctr.expr, target->table.type)) {
+ Alert("Proxy '%s': stick-table '%s' uses a type incompatible with the 'track-sc%d' rule.\n",
+ curproxy->id, hrqrule->act_prm.trk_ctr.table.n ? hrqrule->act_prm.trk_ctr.table.n : curproxy->id,
+ http_req_trk_idx(hrqrule->action));
+ cfgerr++;
+ }
+ else {
+ free(hrqrule->act_prm.trk_ctr.table.n);
+ hrqrule->act_prm.trk_ctr.table.t = &target->table;
+ /* Note: if we decide to enhance the track-sc syntax, we may be able
+ * to pass a list of counters to track and allocate them right here using
+ * stktable_alloc_data_type().
+ */
+ }
+ }
+
/* move any "block" rules at the beginning of the http-request rules */
if (!LIST_ISEMPTY(&curproxy->block_rules)) {
/* insert block_rules into http_req_rules at the beginning */
case HTTP_REQ_ACT_CUSTOM_STOP:
rule->action_ptr(rule, px, s, txn);
return HTTP_RULE_RES_DONE;
+
+ case HTTP_REQ_ACT_TRK_SC0 ... HTTP_REQ_ACT_TRK_SCMAX:
+ /* Note: only the first valid tracking parameter of each
+ * applies.
+ */
+
+ if (stkctr_entry(&s->stkctr[http_req_trk_idx(rule->action)]) == NULL) {
+ struct stktable *t;
+ struct stksess *ts;
+ struct stktable_key *key;
+ void *ptr;
+
+ t = rule->act_prm.trk_ctr.table.t;
+ key = stktable_fetch_key(t, s->be, s, &s->txn, SMP_OPT_DIR_REQ | SMP_OPT_FINAL, rule->act_prm.trk_ctr.expr, NULL);
+
+ if (key && (ts = stktable_get_entry(t, key))) {
+ session_track_stkctr(&s->stkctr[http_req_trk_idx(rule->action)], t, ts);
+
+ /* let's count a new HTTP request as it's the first time we do it */
+ ptr = stktable_data_ptr(t, ts, STKTABLE_DT_HTTP_REQ_CNT);
+ if (ptr)
+ stktable_data_cast(ptr, http_req_cnt)++;
+
+ ptr = stktable_data_ptr(t, ts, STKTABLE_DT_HTTP_REQ_RATE);
+ if (ptr)
+ update_freq_ctr_period(&stktable_data_cast(ptr, http_req_rate),
+ t->data_arg[STKTABLE_DT_HTTP_REQ_RATE].u, 1);
+
+ stkctr_set_flags(&s->stkctr[http_req_trk_idx(rule->action)], STKCTR_TRACK_CONTENT);
+ if (s->fe != s->be)
+ stkctr_set_flags(&s->stkctr[http_req_trk_idx(rule->action)], STKCTR_TRACK_BACKEND);
+ }
+ }
}
}
proxy->conf.lfs_file = strdup(proxy->conf.args.file);
proxy->conf.lfs_line = proxy->conf.args.line;
cur_arg += 1;
+ } else if (strncmp(args[0], "track-sc", 8) == 0 &&
+ args[0][9] == '\0' && args[0][8] >= '0' &&
+ args[0][8] <= '0' + MAX_SESS_STKCTR) { /* track-sc 0..9 */
+ struct sample_expr *expr;
+ unsigned int where;
+ char *err = NULL;
+
+ cur_arg = 1;
+ proxy->conf.args.ctx = ARGC_TRK;
+
+ expr = sample_parse_expr((char **)args, &cur_arg, file, linenum, &err, &proxy->conf.args);
+ if (!expr) {
+ Alert("parsing [%s:%d] : error detected in %s '%s' while parsing 'http-request %s' rule : %s.\n",
+ file, linenum, proxy_type_str(proxy), proxy->id, args[0], err);
+ free(err);
+ goto out_err;
+ }
+
+ where = 0;
+ if (proxy->cap & PR_CAP_FE)
+ where |= SMP_VAL_FE_HRQ_HDR;
+ if (proxy->cap & PR_CAP_BE)
+ where |= SMP_VAL_BE_HRQ_HDR;
+
+ if (!(expr->fetch->val & where)) {
+ Alert("parsing [%s:%d] : error detected in %s '%s' while parsing 'http-request %s' rule :"
+ " fetch method '%s' extracts information from '%s', none of which is available here.\n",
+ file, linenum, proxy_type_str(proxy), proxy->id, args[0],
+ args[cur_arg-1], sample_src_names(expr->fetch->use));
+ free(expr);
+ goto out_err;
+ }
+
+ if (strcmp(args[cur_arg], "table") == 0) {
+ cur_arg++;
+ if (!args[cur_arg]) {
+ Alert("parsing [%s:%d] : error detected in %s '%s' while parsing 'http-request %s' rule : missing table name.\n",
+ file, linenum, proxy_type_str(proxy), proxy->id, args[0]);
+ free(expr);
+ goto out_err;
+ }
+ /* we copy the table name for now, it will be resolved later */
+ rule->act_prm.trk_ctr.table.n = strdup(args[cur_arg]);
+ cur_arg++;
+ }
+ rule->act_prm.trk_ctr.expr = expr;
+ rule->action = HTTP_REQ_ACT_TRK_SC0 + args[0][8] - '0';
} else if (strcmp(args[0], "redirect") == 0) {
struct redirect_rule *redir;
char *errmsg = NULL;