]> git.ipfire.org Git - thirdparty/haproxy.git/commitdiff
MEDIUM: http-rules: Register an action keyword for all http rules
authorChristopher Faulet <cfaulet@haproxy.com>
Thu, 12 Dec 2019 15:40:30 +0000 (16:40 +0100)
committerChristopher Faulet <cfaulet@haproxy.com>
Mon, 20 Jan 2020 14:18:45 +0000 (15:18 +0100)
There are many specific http actions that don't use the action registration
mechanism (allow, deny, set-header...). Instead, the parsing of these actions is
inlined in the functions responsible to parse the http-request/http-response
rules. There is no reason to not register an action keyword for all these
actions. It it the purpose of this patch. The new functions responsible to parse
these http actions are defined in http_act.c

src/http_act.c
src/http_rules.c

index c8d9220feb06c72da33d934c547b583eb3459e49..e86b6160d8962c32c76a8a382b028c06e4e86e32 100644 (file)
@@ -16,6 +16,7 @@
 #include <string.h>
 #include <time.h>
 
+#include <common/cfgparse.h>
 #include <common/chunk.h>
 #include <common/compat.h>
 #include <common/config.h>
@@ -31,6 +32,7 @@
 
 #include <proto/acl.h>
 #include <proto/arg.h>
+#include <proto/action.h>
 #include <proto/http_rules.h>
 #include <proto/http_htx.h>
 #include <proto/log.h>
@@ -691,21 +693,509 @@ static enum act_parse_ret parse_http_res_capture(const char **args, int *orig_ar
        return ACT_RET_PRS_OK;
 }
 
+/* Parse a "allow" action for a request or a response rule. It takes no argument. It
+ * returns ACT_RET_PRS_OK on success, ACT_RET_PRS_ERR on error.
+ */
+static enum act_parse_ret parse_http_allow(const char **args, int *orig_arg, struct proxy *px,
+                                          struct act_rule *rule, char **err)
+{
+       rule->action = ACT_ACTION_ALLOW;
+       return ACT_RET_PRS_OK;
+}
+
+/* Parse "deny" or "tarpit" actions for a request rule. It may take 2 optional arguments
+ * to define the status code. It returns ACT_RET_PRS_OK on success,
+ * ACT_RET_PRS_ERR on error.
+ */
+static enum act_parse_ret parse_http_req_deny(const char **args, int *orig_arg, struct proxy *px,
+                                             struct act_rule *rule, char **err)
+{
+       int code, hc, cur_arg;
+
+       cur_arg = *orig_arg;
+       if (!strcmp(args[cur_arg-1], "tarpit")) {
+               rule->action = ACT_HTTP_REQ_TARPIT;
+               rule->deny_status = HTTP_ERR_500;
+       }
+       else {
+               rule->action = ACT_ACTION_DENY;
+               rule->deny_status = HTTP_ERR_403;
+       }
+
+       if (strcmp(args[cur_arg], "deny_status") == 0) {
+               cur_arg++;
+               if (!*args[cur_arg]) {
+                       memprintf(err, "missing status code.\n");
+                       return ACT_RET_PRS_ERR;
+               }
+
+               code = atol(args[cur_arg]);
+               cur_arg++;
+               for (hc = 0; hc < HTTP_ERR_SIZE; hc++) {
+                       if (http_err_codes[hc] == code) {
+                               rule->deny_status = hc;
+                               break;
+                       }
+               }
+               if (hc >= HTTP_ERR_SIZE)
+                       memprintf(err, "status code %d not handled, using default code %d",
+                                 code, http_err_codes[rule->deny_status]);
+       }
+
+       *orig_arg = cur_arg;
+       return ACT_RET_PRS_OK;
+}
+
+/* Parse a "deny" action for a response rule. It takes no argument. It returns
+ * ACT_RET_PRS_OK on success, ACT_RET_PRS_ERR on error.
+ */
+static enum act_parse_ret parse_http_res_deny(const char **args, int *orig_arg, struct proxy *px,
+                                             struct act_rule *rule, char **err)
+{
+       rule->action = ACT_ACTION_DENY;
+       return ACT_RET_PRS_OK;
+}
+
+/* Parse a "auth" action. It may take 2 optional arguments to define a "realm"
+ * parameter. It returns ACT_RET_PRS_OK on success, ACT_RET_PRS_ERR on error.
+ */
+static enum act_parse_ret parse_http_auth(const char **args, int *orig_arg, struct proxy *px,
+                                         struct act_rule *rule, char **err)
+{
+       int cur_arg;
+
+       rule->action = ACT_HTTP_REQ_AUTH;
+
+       cur_arg = *orig_arg;
+       if (!strcmp(args[cur_arg], "realm")) {
+               cur_arg++;
+               if (!*args[cur_arg]) {
+                       memprintf(err, "missing realm value.\n");
+                       return ACT_RET_PRS_ERR;
+               }
+               rule->arg.auth.realm = strdup(args[cur_arg]);
+               cur_arg++;
+       }
+
+       *orig_arg = cur_arg;
+       return ACT_RET_PRS_OK;
+}
+
+/* Parse a "set-nice" action. It takes the nice value as argument. It returns
+ * ACT_RET_PRS_OK on success, ACT_RET_PRS_ERR on error.
+ */
+static enum act_parse_ret parse_http_set_nice(const char **args, int *orig_arg, struct proxy *px,
+                                             struct act_rule *rule, char **err)
+{
+       int cur_arg;
+
+       rule->action = ACT_HTTP_SET_NICE;
+
+       cur_arg = *orig_arg;
+       if (!*args[cur_arg]) {
+               memprintf(err, "expects exactly 1 argument (integer value)");
+               return ACT_RET_PRS_ERR;
+       }
+       rule->arg.nice = atoi(args[cur_arg]);
+       if (rule->arg.nice < -1024)
+               rule->arg.nice = -1024;
+       else if (rule->arg.nice > 1024)
+               rule->arg.nice = 1024;
+
+       *orig_arg = cur_arg + 1;
+       return ACT_RET_PRS_OK;
+}
+
+/* Parse a "set-tos" action. It takes the TOS value as argument. It returns
+ * ACT_RET_PRS_OK on success, ACT_RET_PRS_ERR on error.
+ */
+static enum act_parse_ret parse_http_set_tos(const char **args, int *orig_arg, struct proxy *px,
+                                             struct act_rule *rule, char **err)
+{
+#ifdef IP_TOS
+       char *endp;
+       int cur_arg;
+
+       rule->action = ACT_HTTP_SET_TOS;
+
+       cur_arg = *orig_arg;
+       if (!*args[cur_arg]) {
+               memprintf(err, "expects exactly 1 argument (integer/hex value)");
+               return ACT_RET_PRS_ERR;
+       }
+       rule->arg.tos = strtol(args[cur_arg], &endp, 0);
+       if (endp && *endp != '\0') {
+               memprintf(err, "invalid character starting at '%s' (integer/hex value expected)", endp);
+               return ACT_RET_PRS_ERR;
+       }
+
+       *orig_arg = cur_arg + 1;
+       return ACT_RET_PRS_OK;
+#else
+       memprintf(err, "not supported on this platform (IP_TOS undefined)");
+       return ACT_RET_PRS_ERR;
+#endif
+}
+
+/* Parse a "set-mark" action. It takes the MARK value as argument. It returns
+ * ACT_RET_PRS_OK on success, ACT_RET_PRS_ERR on error.
+ */
+static enum act_parse_ret parse_http_set_mark(const char **args, int *orig_arg, struct proxy *px,
+                                             struct act_rule *rule, char **err)
+{
+#ifdef SO_MARK
+       char *endp;
+       int cur_arg;
+
+       rule->action = ACT_HTTP_SET_MARK;
+
+       cur_arg = *orig_arg;
+       if (!*args[cur_arg]) {
+               memprintf(err, "expects exactly 1 argument (integer/hex value)");
+               return ACT_RET_PRS_ERR;
+       }
+       rule->arg.mark = strtoul(args[cur_arg], &endp, 0);
+       if (endp && *endp != '\0') {
+               memprintf(err, "invalid character starting at '%s' (integer/hex value expected)", endp);
+               return ACT_RET_PRS_ERR;
+       }
+
+       *orig_arg = cur_arg + 1;
+       global.last_checks |= LSTCHK_NETADM;
+       return ACT_RET_PRS_OK;
+#else
+       memprintf(err, "not supported on this platform (SO_MARK undefined)");
+       return ACT_RET_PRS_ERR;
+#endif
+}
+
+/* Parse a "set-log-level" action. It takes the level value as argument. It
+ * returns ACT_RET_PRS_OK on success, ACT_RET_PRS_ERR on error.
+ */
+static enum act_parse_ret parse_http_set_log_level(const char **args, int *orig_arg, struct proxy *px,
+                                                  struct act_rule *rule, char **err)
+{
+       int cur_arg;
+
+       rule->action = ACT_HTTP_SET_LOGL;
+
+       cur_arg = *orig_arg;
+       if (!*args[cur_arg]) {
+         bad_log_level:
+               memprintf(err, "expects exactly 1 argument (log level name or 'silent')");
+               return ACT_RET_PRS_ERR;
+       }
+       if (strcmp(args[cur_arg], "silent") == 0)
+               rule->arg.loglevel = -1;
+       else if ((rule->arg.loglevel = get_log_level(args[cur_arg]) + 1) == 0)
+               goto bad_log_level;
+
+       *orig_arg = cur_arg + 1;
+       return ACT_RET_PRS_OK;
+}
+
+/* Parse a "set-header", "add-header" or "early-hint" actions. It takes an
+ * header name and a log-format string as arguments. It returns ACT_RET_PRS_OK
+ * on success, ACT_RET_PRS_ERR on error.
+ *
+ * Note: same function is used for the request and the response. However
+ * "early-hint" rules are only supported for request rules.
+ */
+static enum act_parse_ret parse_http_set_header(const char **args, int *orig_arg, struct proxy *px,
+                                                  struct act_rule *rule, char **err)
+{
+       char **hdr_name;
+       int *hdr_name_len;
+       struct list *fmt;
+       int cap, cur_arg;
+
+       rule->action = (*args[*orig_arg-1] == 'a' ? ACT_HTTP_ADD_HDR :
+                       *args[*orig_arg-1] == 's' ? ACT_HTTP_SET_HDR : ACT_HTTP_EARLY_HINT);
+
+       cur_arg = *orig_arg;
+       if (!*args[cur_arg] || !*args[cur_arg+1]) {
+               memprintf(err, "expects exactly 2 arguments");
+               return ACT_RET_PRS_ERR;
+       }
+
+       hdr_name     = (*args[cur_arg-1] == 'e' ? &rule->arg.early_hint.name     : &rule->arg.hdr_add.name);
+       hdr_name_len = (*args[cur_arg-1] == 'e' ? &rule->arg.early_hint.name_len : &rule->arg.hdr_add.name_len);
+       fmt          = (*args[cur_arg-1] == 'e' ? &rule->arg.early_hint.fmt      : &rule->arg.hdr_add.fmt);
+
+       *hdr_name = strdup(args[cur_arg]);
+       *hdr_name_len = strlen(*hdr_name);
+       LIST_INIT(fmt);
+
+       if (rule->from == ACT_F_HTTP_REQ) {
+               px->conf.args.ctx = ARGC_HRQ;
+               cap = (px->cap & PR_CAP_FE) ? SMP_VAL_FE_HRQ_HDR : SMP_VAL_BE_HRQ_HDR;
+       }
+       else{
+               px->conf.args.ctx =  ARGC_HRS;
+               cap = (px->cap & PR_CAP_BE) ? SMP_VAL_BE_HRS_HDR : SMP_VAL_FE_HRS_HDR;
+       }
+
+       cur_arg++;
+       if (!parse_logformat_string(args[cur_arg], px, fmt, LOG_OPT_HTTP, cap, err))
+               return ACT_RET_PRS_ERR;
+
+       free(px->conf.lfs_file);
+       px->conf.lfs_file = strdup(px->conf.args.file);
+       px->conf.lfs_line = px->conf.args.line;
+
+       *orig_arg = cur_arg + 1;
+       return ACT_RET_PRS_OK;
+}
+
+/* Parse a "replace-header" or "replace-value" actions. It takes an header name,
+ * a regex and replacement string as arguments. It returns ACT_RET_PRS_OK on
+ * success, ACT_RET_PRS_ERR on error.
+ */
+static enum act_parse_ret parse_http_replace_header(const char **args, int *orig_arg, struct proxy *px,
+                                                   struct act_rule *rule, char **err)
+{
+       int cap, cur_arg;
+
+       rule->action = args[*orig_arg-1][8] == 'h' ? ACT_HTTP_REPLACE_HDR : ACT_HTTP_REPLACE_VAL;
+
+       cur_arg = *orig_arg;
+       if (!*args[cur_arg] || !*args[cur_arg+1] || !*args[cur_arg+2]) {
+               memprintf(err, "expects exactly 3 arguments");
+               return ACT_RET_PRS_ERR;
+       }
+
+       rule->arg.hdr_add.name = strdup(args[cur_arg]);
+       rule->arg.hdr_add.name_len = strlen(rule->arg.hdr_add.name);
+       LIST_INIT(&rule->arg.hdr_add.fmt);
+
+       cur_arg++;
+       if (!(rule->arg.hdr_add.re = regex_comp(args[cur_arg], 1, 1, err)))
+               return ACT_RET_PRS_ERR;
+
+       if (rule->from == ACT_F_HTTP_REQ) {
+               px->conf.args.ctx = ARGC_HRQ;
+               cap = (px->cap & PR_CAP_FE) ? SMP_VAL_FE_HRQ_HDR : SMP_VAL_BE_HRQ_HDR;
+       }
+       else{
+               px->conf.args.ctx =  ARGC_HRS;
+               cap = (px->cap & PR_CAP_BE) ? SMP_VAL_BE_HRS_HDR : SMP_VAL_FE_HRS_HDR;
+       }
+
+       cur_arg++;
+       if (!parse_logformat_string(args[cur_arg], px, &rule->arg.hdr_add.fmt, LOG_OPT_HTTP, cap, err))
+               return ACT_RET_PRS_ERR;
+
+       free(px->conf.lfs_file);
+       px->conf.lfs_file = strdup(px->conf.args.file);
+       px->conf.lfs_line = px->conf.args.line;
+
+       *orig_arg = cur_arg + 1;
+       return ACT_RET_PRS_OK;
+}
+
+/* Parse a "del-header" action. It takes an header name as argument. It returns
+ * ACT_RET_PRS_OK on success, ACT_RET_PRS_ERR on error.
+ */
+static enum act_parse_ret parse_http_del_header(const char **args, int *orig_arg, struct proxy *px,
+                                               struct act_rule *rule, char **err)
+{
+       int cur_arg;
+
+       rule->action = ACT_HTTP_DEL_HDR;
+
+       cur_arg = *orig_arg;
+       if (!*args[cur_arg]) {
+               memprintf(err, "expects exactly 1 arguments");
+               return ACT_RET_PRS_ERR;
+       }
+
+       rule->arg.hdr_add.name = strdup(args[cur_arg]);
+       rule->arg.hdr_add.name_len = strlen(rule->arg.hdr_add.name);
+
+       px->conf.args.ctx = (rule->from == ACT_F_HTTP_REQ ? ARGC_HRQ : ARGC_HRS);
+
+       *orig_arg = cur_arg + 1;
+       return ACT_RET_PRS_OK;
+}
+
+/* Parse a "redirect" action. It returns ACT_RET_PRS_OK on success,
+ * ACT_RET_PRS_ERR on error.
+ */
+static enum act_parse_ret parse_http_redirect(const char **args, int *orig_arg, struct proxy *px,
+                                             struct act_rule *rule, char **err)
+{
+       struct redirect_rule *redir;
+       int dir, cur_arg;
+
+       rule->action = ACT_HTTP_REDIR;
+
+       cur_arg = *orig_arg;
+
+       dir = (rule->from == ACT_F_HTTP_REQ ? 0 : 1);
+       if ((redir = http_parse_redirect_rule(px->conf.args.file, px->conf.args.line, px, &args[cur_arg], err, 1, dir)) == NULL)
+               return ACT_RET_PRS_ERR;
+
+       rule->arg.redir = redir;
+       rule->cond = redir->cond;
+       redir->cond = NULL;
+
+       /* skip all arguments */
+       while (*args[cur_arg])
+               cur_arg++;
+
+       *orig_arg = cur_arg;
+       return ACT_RET_PRS_OK;
+}
+
+/* Parse a "add-acl", "del-acl", "set-map" or "del-map" actions. It takes one or
+ * two log-format string as argument depending on the action. It returns
+ * ACT_RET_PRS_OK on success, ACT_RET_PRS_ERR on error.
+ */
+static enum act_parse_ret parse_http_set_map(const char **args, int *orig_arg, struct proxy *px,
+                                            struct act_rule *rule, char **err)
+{
+       int cap, cur_arg;
+
+       rule->action = (args[*orig_arg-1][0] == 'a' ? ACT_HTTP_ADD_ACL :
+                       (args[*orig_arg-1][0] == 's' ? ACT_HTTP_SET_MAP :
+                        (args[*orig_arg-1][4] == 'a' ? ACT_HTTP_DEL_ACL : ACT_HTTP_DEL_MAP)));
+
+       cur_arg = *orig_arg;
+       if (rule->action == ACT_HTTP_SET_MAP && (!*args[cur_arg] || !*args[cur_arg+1])) {
+               memprintf(err, "expects exactly 2 arguments");
+               return ACT_RET_PRS_ERR;
+       }
+       else if (!*args[cur_arg]) {
+               memprintf(err, "expects exactly 1 arguments");
+               return ACT_RET_PRS_ERR;
+       }
+
+       /*
+        * '+ 8' for 'set-map(' (same for del-map)
+        * '- 9' for 'set-map(' + trailing ')'  (same for del-map)
+        */
+       rule->arg.map.ref = my_strndup(args[cur_arg-1] + 8, strlen(args[cur_arg-1]) - 9);
+
+       if (rule->from == ACT_F_HTTP_REQ) {
+               px->conf.args.ctx = ARGC_HRQ;
+               cap = (px->cap & PR_CAP_FE) ? SMP_VAL_FE_HRQ_HDR : SMP_VAL_BE_HRQ_HDR;
+       }
+       else{
+               px->conf.args.ctx =  ARGC_HRS;
+               cap = (px->cap & PR_CAP_BE) ? SMP_VAL_BE_HRS_HDR : SMP_VAL_FE_HRS_HDR;
+       }
+
+       /* key pattern */
+       LIST_INIT(&rule->arg.map.key);
+       if (!parse_logformat_string(args[cur_arg], px, &rule->arg.map.key, LOG_OPT_HTTP, cap, err))
+               return ACT_RET_PRS_ERR;
+
+       if (rule->action == ACT_HTTP_SET_MAP) {
+               /* value pattern for set-map only */
+               cur_arg++;
+               LIST_INIT(&rule->arg.map.value);
+               if (!parse_logformat_string(args[cur_arg], px, &rule->arg.map.value, LOG_OPT_HTTP, cap, err))
+                       return ACT_RET_PRS_ERR;
+       }
+
+       free(px->conf.lfs_file);
+       px->conf.lfs_file = strdup(px->conf.args.file);
+       px->conf.lfs_line = px->conf.args.line;
+
+       *orig_arg = cur_arg + 1;
+       return ACT_RET_PRS_OK;
+}
+
+
+/* Parse a "track-sc*" actions. It returns ACT_RET_PRS_OK on success,
+ * ACT_RET_PRS_ERR on error.
+ */
+static enum act_parse_ret parse_http_track_sc(const char **args, int *orig_arg, struct proxy *px,
+                                                struct act_rule *rule, char **err)
+{
+       struct sample_expr *expr;
+       unsigned int where;
+       unsigned int tsc_num;
+       const char *tsc_num_str;
+       int cur_arg;
+
+       tsc_num_str = &args[*orig_arg-1][8];
+       if (cfg_parse_track_sc_num(&tsc_num, tsc_num_str, tsc_num_str + strlen(tsc_num_str), err) == -1)
+               return ACT_RET_PRS_ERR;
+
+       cur_arg = *orig_arg;
+       expr = sample_parse_expr((char **)args, &cur_arg, px->conf.args.file, px->conf.args.line,
+                                err, &px->conf.args);
+       if (!expr)
+               return ACT_RET_PRS_ERR;
+
+       where = 0;
+       if (px->cap & PR_CAP_FE)
+               where |= (rule->from == ACT_F_HTTP_REQ ? SMP_VAL_FE_HRQ_HDR : SMP_VAL_FE_HRS_HDR);
+       if (px->cap & PR_CAP_BE)
+               where |= (rule->from == ACT_F_HTTP_REQ ? SMP_VAL_BE_HRQ_HDR : SMP_VAL_BE_HRS_HDR);
+
+       if (!(expr->fetch->val & where)) {
+               memprintf(err, "fetch method '%s' extracts information from '%s', none of which is available here",
+                         args[cur_arg-1], sample_src_names(expr->fetch->use));
+               return ACT_RET_PRS_ERR;
+       }
+
+       if (strcmp(args[cur_arg], "table") == 0) {
+               cur_arg++;
+               if (!*args[cur_arg]) {
+                       memprintf(err, "missing table name");
+                       return ACT_RET_PRS_ERR;
+               }
+
+               /* we copy the table name for now, it will be resolved later */
+               rule->arg.trk_ctr.table.n = strdup(args[cur_arg]);
+               cur_arg++;
+       }
+
+       rule->arg.trk_ctr.expr = expr;
+       rule->action = ACT_ACTION_TRK_SC0 + tsc_num;
+       rule->check_ptr = check_trk_action;
+
+       *orig_arg = cur_arg;
+       return ACT_RET_PRS_OK;
+}
+
 /************************************************************************/
 /*   All supported http-request action keywords must be declared here.  */
 /************************************************************************/
 
 static struct action_kw_list http_req_actions = {
        .kw = {
-               { "capture",    parse_http_req_capture },
-               { "reject",     parse_http_action_reject },
-               { "disable-l7-retry", parse_http_req_disable_l7_retry },
-               { "replace-path", parse_replace_uri },
-               { "replace-uri", parse_replace_uri },
-               { "set-method", parse_set_req_line },
-               { "set-path",   parse_set_req_line },
-               { "set-query",  parse_set_req_line },
-               { "set-uri",    parse_set_req_line },
+               { "add-acl",          parse_http_set_map,              1 },
+               { "add-header",       parse_http_set_header,           0 },
+               { "allow",            parse_http_allow,                0 },
+               { "auth",             parse_http_auth,                 0 },
+               { "capture",          parse_http_req_capture,          0 },
+               { "del-acl",          parse_http_set_map,              1 },
+               { "del-header",       parse_http_del_header,           0 },
+               { "del-map",          parse_http_set_map,              1 },
+               { "deny",             parse_http_req_deny,             0 },
+               { "disable-l7-retry", parse_http_req_disable_l7_retry, 0 },
+               { "early-hint",       parse_http_set_header,           0 },
+               { "redirect",         parse_http_redirect,             0 },
+               { "reject",           parse_http_action_reject,        0 },
+               { "replace-header",   parse_http_replace_header,       0 },
+               { "replace-path",     parse_replace_uri,               0 },
+               { "replace-uri",      parse_replace_uri,               0 },
+               { "replace-value",    parse_http_replace_header,       0 },
+               { "set-header",       parse_http_set_header,           0 },
+               { "set-log-level",    parse_http_set_log_level,        0 },
+               { "set-map",          parse_http_set_map,              1 },
+               { "set-method",       parse_set_req_line,              0 },
+               { "set-mark",         parse_http_set_mark,             0 },
+               { "set-nice",         parse_http_set_nice,             0 },
+               { "set-path",         parse_set_req_line,              0 },
+               { "set-query",        parse_set_req_line,              0 },
+               { "set-tos",          parse_http_set_tos,              0 },
+               { "set-uri",          parse_set_req_line,              0 },
+               { "tarpit",           parse_http_req_deny,             0 },
+               { "track-sc",         parse_http_track_sc,             1 },
                { NULL, NULL }
        }
 };
@@ -714,8 +1204,25 @@ INITCALL1(STG_REGISTER, http_req_keywords_register, &http_req_actions);
 
 static struct action_kw_list http_res_actions = {
        .kw = {
-               { "capture",    parse_http_res_capture },
-               { "set-status", parse_http_set_status },
+               { "add-acl",         parse_http_set_map,        1 },
+               { "add-header",      parse_http_set_header,     0 },
+               { "allow",           parse_http_allow,          0 },
+               { "capture",         parse_http_res_capture,    0 },
+               { "del-acl",         parse_http_set_map,        1 },
+               { "del-header",      parse_http_del_header,     0 },
+               { "del-map",         parse_http_set_map,        1 },
+               { "deny",            parse_http_res_deny,       0 },
+               { "redirect",        parse_http_redirect,       0 },
+               { "replace-header",  parse_http_replace_header, 0 },
+               { "replace-value",   parse_http_replace_header, 0 },
+               { "set-header",      parse_http_set_header,     0 },
+               { "set-log-level",   parse_http_set_log_level,  0 },
+               { "set-map",         parse_http_set_map,        1 },
+               { "set-mark",        parse_http_set_mark,       0 },
+               { "set-nice",        parse_http_set_nice,       0 },
+               { "set-status",      parse_http_set_status,     0 },
+               { "set-tos",         parse_http_set_tos,        0 },
+               { "track-sc",        parse_http_track_sc,       1 },
                { NULL, NULL }
        }
 };
index aad771466dbd95fdbcfeaf3fac62f7661945b57e..2e70e6bf0f03262c18dededb1bc794a9e05c0c73 100644 (file)
@@ -69,462 +69,19 @@ struct act_rule *parse_http_req_cond(const char **args, const char *file, int li
        struct act_rule *rule;
        struct action_kw *custom = NULL;
        int cur_arg;
-       char *error;
 
        rule = calloc(1, sizeof(*rule));
        if (!rule) {
                ha_alert("parsing [%s:%d]: out of memory.\n", file, linenum);
                goto out_err;
        }
+       rule->from = ACT_F_HTTP_REQ;
 
-       if (!strcmp(args[0], "allow")) {
-               rule->action = ACT_ACTION_ALLOW;
-               cur_arg = 1;
-       } else if (!strcmp(args[0], "deny") || !strcmp(args[0], "block") || !strcmp(args[0], "tarpit")) {
-               int code;
-               int hc;
-
-               if (!strcmp(args[0], "tarpit")) {
-                   rule->action = ACT_HTTP_REQ_TARPIT;
-                   rule->deny_status = HTTP_ERR_500;
-               }
-               else {
-                       rule->action = ACT_ACTION_DENY;
-                       rule->deny_status = HTTP_ERR_403;
-               }
-               cur_arg = 1;
-                if (strcmp(args[cur_arg], "deny_status") == 0) {
-                        cur_arg++;
-                        if (!args[cur_arg]) {
-                                ha_alert("parsing [%s:%d] : error detected in %s '%s' while parsing 'http-request %s' rule : missing status code.\n",
-                                        file, linenum, proxy_type_str(proxy), proxy->id, args[0]);
-                                goto out_err;
-                        }
-
-                        code = atol(args[cur_arg]);
-                        cur_arg++;
-                        for (hc = 0; hc < HTTP_ERR_SIZE; hc++) {
-                                if (http_err_codes[hc] == code) {
-                                        rule->deny_status = hc;
-                                        break;
-                                }
-                        }
-
-                        if (hc >= HTTP_ERR_SIZE) {
-                                ha_warning("parsing [%s:%d] : status code %d not handled, using default code %d.\n",
-                                          file, linenum, code, http_err_codes[rule->deny_status]);
-                        }
-                }
-       } else if (!strcmp(args[0], "auth")) {
-               rule->action = ACT_HTTP_REQ_AUTH;
-               cur_arg = 1;
-
-               while(*args[cur_arg]) {
-                       if (!strcmp(args[cur_arg], "realm")) {
-                               rule->arg.auth.realm = strdup(args[cur_arg + 1]);
-                               cur_arg+=2;
-                               continue;
-                       } else
-                               break;
-               }
-       } else if (!strcmp(args[0], "set-nice")) {
-               rule->action = ACT_HTTP_SET_NICE;
-               cur_arg = 1;
-
-               if (!*args[cur_arg] ||
-                   (*args[cur_arg + 1] && strcmp(args[cur_arg + 1], "if") != 0 && strcmp(args[cur_arg + 1], "unless") != 0)) {
-                       ha_alert("parsing [%s:%d]: 'http-request %s' expects exactly 1 argument (integer value).\n",
-                                file, linenum, args[0]);
-                       goto out_err;
-               }
-               rule->arg.nice = atoi(args[cur_arg]);
-               if (rule->arg.nice < -1024)
-                       rule->arg.nice = -1024;
-               else if (rule->arg.nice > 1024)
-                       rule->arg.nice = 1024;
-               cur_arg++;
-       } else if (!strcmp(args[0], "set-tos")) {
-#ifdef IP_TOS
-               char *err;
-               rule->action = ACT_HTTP_SET_TOS;
-               cur_arg = 1;
-
-               if (!*args[cur_arg] ||
-                   (*args[cur_arg + 1] && strcmp(args[cur_arg + 1], "if") != 0 && strcmp(args[cur_arg + 1], "unless") != 0)) {
-                       ha_alert("parsing [%s:%d]: 'http-request %s' expects exactly 1 argument (integer/hex value).\n",
-                                file, linenum, args[0]);
-                       goto out_err;
-               }
-
-               rule->arg.tos = strtol(args[cur_arg], &err, 0);
-               if (err && *err != '\0') {
-                       ha_alert("parsing [%s:%d]: invalid character starting at '%s' in 'http-request %s' (integer/hex value expected).\n",
-                                file, linenum, err, args[0]);
-                       goto out_err;
-               }
-               cur_arg++;
-#else
-               ha_alert("parsing [%s:%d]: 'http-request %s' is not supported on this platform (IP_TOS undefined).\n", file, linenum, args[0]);
-               goto out_err;
-#endif
-       } else if (!strcmp(args[0], "set-mark")) {
-#ifdef SO_MARK
-               char *err;
-               rule->action = ACT_HTTP_SET_MARK;
-               cur_arg = 1;
-
-               if (!*args[cur_arg] ||
-                   (*args[cur_arg + 1] && strcmp(args[cur_arg + 1], "if") != 0 && strcmp(args[cur_arg + 1], "unless") != 0)) {
-                       ha_alert("parsing [%s:%d]: 'http-request %s' expects exactly 1 argument (integer/hex value).\n",
-                                file, linenum, args[0]);
-                       goto out_err;
-               }
-
-               rule->arg.mark = strtoul(args[cur_arg], &err, 0);
-               if (err && *err != '\0') {
-                       ha_alert("parsing [%s:%d]: invalid character starting at '%s' in 'http-request %s' (integer/hex value expected).\n",
-                                file, linenum, err, args[0]);
-                       goto out_err;
-               }
-               cur_arg++;
-               global.last_checks |= LSTCHK_NETADM;
-#else
-               ha_alert("parsing [%s:%d]: 'http-request %s' is not supported on this platform (SO_MARK undefined).\n", file, linenum, args[0]);
-               goto out_err;
-#endif
-       } else if (!strcmp(args[0], "set-log-level")) {
-               rule->action = ACT_HTTP_SET_LOGL;
-               cur_arg = 1;
-
-               if (!*args[cur_arg] ||
-                   (*args[cur_arg + 1] && strcmp(args[cur_arg + 1], "if") != 0 && strcmp(args[cur_arg + 1], "unless") != 0)) {
-               bad_log_level:
-                       ha_alert("parsing [%s:%d]: 'http-request %s' expects exactly 1 argument (log level name or 'silent').\n",
-                                file, linenum, args[0]);
-                       goto out_err;
-               }
-               if (strcmp(args[cur_arg], "silent") == 0)
-                       rule->arg.loglevel = -1;
-               else if ((rule->arg.loglevel = get_log_level(args[cur_arg]) + 1) == 0)
-                       goto bad_log_level;
-               cur_arg++;
-       } else if (strcmp(args[0], "add-header") == 0 || strcmp(args[0], "set-header") == 0 ||
-                  strcmp(args[0], "early-hint") == 0) {
-               char **hdr_name;
-               int *hdr_name_len;
-               struct list *fmt;
-
-               rule->action = *args[0] == 'a' ? ACT_HTTP_ADD_HDR :
-                              *args[0] == 's' ? ACT_HTTP_SET_HDR : ACT_HTTP_EARLY_HINT;
-
-               hdr_name     = *args[0] == 'e' ? &rule->arg.early_hint.name     : &rule->arg.hdr_add.name;
-               hdr_name_len = *args[0] == 'e' ? &rule->arg.early_hint.name_len : &rule->arg.hdr_add.name_len;
-               fmt          = *args[0] == 'e' ? &rule->arg.early_hint.fmt      : &rule->arg.hdr_add.fmt;
-
-               cur_arg = 1;
-
-               if (!*args[cur_arg] || !*args[cur_arg+1] ||
-                   (*args[cur_arg+2] && strcmp(args[cur_arg+2], "if") != 0 && strcmp(args[cur_arg+2], "unless") != 0)) {
-                       ha_alert("parsing [%s:%d]: 'http-request %s' expects exactly 2 arguments.\n",
-                                file, linenum, args[0]);
-                       goto out_err;
-               }
-
-               *hdr_name = strdup(args[cur_arg]);
-               *hdr_name_len = strlen(*hdr_name);
-               LIST_INIT(fmt);
-
-               proxy->conf.args.ctx = ARGC_HRQ;
-               error = NULL;
-               if (!parse_logformat_string(args[cur_arg + 1], proxy, fmt, LOG_OPT_HTTP,
-                                           (proxy->cap & PR_CAP_FE) ? SMP_VAL_FE_HRQ_HDR : SMP_VAL_BE_HRQ_HDR, &error)) {
-                       ha_alert("parsing [%s:%d]: 'http-request %s': %s.\n",
-                                file, linenum, args[0], error);
-                       free(error);
-                       goto out_err;
-               }
-               free(proxy->conf.lfs_file);
-               proxy->conf.lfs_file = strdup(proxy->conf.args.file);
-               proxy->conf.lfs_line = proxy->conf.args.line;
-               cur_arg += 2;
-       } else if (strcmp(args[0], "replace-header") == 0 || strcmp(args[0], "replace-value") == 0) {
-               rule->action = args[0][8] == 'h' ? ACT_HTTP_REPLACE_HDR : ACT_HTTP_REPLACE_VAL;
-               cur_arg = 1;
-
-               if (!*args[cur_arg] || !*args[cur_arg+1] || !*args[cur_arg+2] ||
-                   (*args[cur_arg+3] && strcmp(args[cur_arg+3], "if") != 0 && strcmp(args[cur_arg+3], "unless") != 0)) {
-                       ha_alert("parsing [%s:%d]: 'http-request %s' expects exactly 3 arguments.\n",
-                                file, linenum, args[0]);
-                       goto out_err;
-               }
-
-               rule->arg.hdr_add.name = strdup(args[cur_arg]);
-               rule->arg.hdr_add.name_len = strlen(rule->arg.hdr_add.name);
-               LIST_INIT(&rule->arg.hdr_add.fmt);
-
-               error = NULL;
-               if (!(rule->arg.hdr_add.re = regex_comp(args[cur_arg + 1], 1, 1, &error))) {
-                       ha_alert("parsing [%s:%d] : '%s' : %s.\n", file, linenum,
-                                args[cur_arg + 1], error);
-                       free(error);
-                       goto out_err;
-               }
-
-               proxy->conf.args.ctx = ARGC_HRQ;
-               error = NULL;
-               if (!parse_logformat_string(args[cur_arg + 2], proxy, &rule->arg.hdr_add.fmt, LOG_OPT_HTTP,
-                                           (proxy->cap & PR_CAP_FE) ? SMP_VAL_FE_HRQ_HDR : SMP_VAL_BE_HRQ_HDR, &error)) {
-                       ha_alert("parsing [%s:%d]: 'http-request %s': %s.\n",
-                                file, linenum, args[0], error);
-                       free(error);
-                       goto out_err;
-               }
-
-               free(proxy->conf.lfs_file);
-               proxy->conf.lfs_file = strdup(proxy->conf.args.file);
-               proxy->conf.lfs_line = proxy->conf.args.line;
-               cur_arg += 3;
-       } else if (strcmp(args[0], "del-header") == 0) {
-               rule->action = ACT_HTTP_DEL_HDR;
-               cur_arg = 1;
-
-               if (!*args[cur_arg] ||
-                   (*args[cur_arg+1] && strcmp(args[cur_arg+1], "if") != 0 && strcmp(args[cur_arg+1], "unless") != 0)) {
-                       ha_alert("parsing [%s:%d]: 'http-request %s' expects exactly 1 argument.\n",
-                                file, linenum, args[0]);
-                       goto out_err;
-               }
-
-               rule->arg.hdr_add.name = strdup(args[cur_arg]);
-               rule->arg.hdr_add.name_len = strlen(rule->arg.hdr_add.name);
-
-               proxy->conf.args.ctx = ARGC_HRQ;
-               free(proxy->conf.lfs_file);
-               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) {
-               struct sample_expr *expr;
-               unsigned int where;
-               char *err = NULL;
-               unsigned int tsc_num;
-               const char *tsc_num_str;
-
-               cur_arg = 1;
-               proxy->conf.args.ctx = ARGC_TRK;
-
-               tsc_num_str = &args[0][8];
-               if (cfg_parse_track_sc_num(&tsc_num, tsc_num_str, tsc_num_str + strlen(tsc_num_str), &err) == -1) {
-                       ha_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;
-               }
-
-               expr = sample_parse_expr((char **)args, &cur_arg, file, linenum, &err, &proxy->conf.args);
-               if (!expr) {
-                       ha_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)) {
-                       ha_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]) {
-                               ha_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->arg.trk_ctr.table.n = strdup(args[cur_arg]);
-                       cur_arg++;
-               }
-               rule->arg.trk_ctr.expr = expr;
-               rule->action = ACT_ACTION_TRK_SC0 + tsc_num;
-               rule->check_ptr = check_trk_action;
-       } else if (strcmp(args[0], "redirect") == 0) {
-               struct redirect_rule *redir;
+       if (((custom = action_http_req_custom(args[0])) != NULL)) {
                char *errmsg = NULL;
 
-               if ((redir = http_parse_redirect_rule(file, linenum, proxy, (const char **)args + 1, &errmsg, 1, 0)) == NULL) {
-                       ha_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], errmsg);
-                       goto out_err;
-               }
-
-               /* this redirect rule might already contain a parsed condition which
-                * we'll pass to the http-request rule.
-                */
-               rule->action = ACT_HTTP_REDIR;
-               rule->arg.redir = redir;
-               rule->cond = redir->cond;
-               redir->cond = NULL;
-               cur_arg = 2;
-               return rule;
-       } else if (strncmp(args[0], "add-acl", 7) == 0) {
-               /* http-request add-acl(<reference (acl name)>) <key pattern> */
-               rule->action = ACT_HTTP_ADD_ACL;
-               /*
-                * '+ 8' for 'add-acl('
-                * '- 9' for 'add-acl(' + trailing ')'
-                */
-               rule->arg.map.ref = my_strndup(args[0] + 8, strlen(args[0]) - 9);
-
-               cur_arg = 1;
-
-               if (!*args[cur_arg] ||
-                   (*args[cur_arg+1] && strcmp(args[cur_arg+1], "if") != 0 && strcmp(args[cur_arg+1], "unless") != 0)) {
-                       ha_alert("parsing [%s:%d]: 'http-request %s' expects exactly 1 argument.\n",
-                                file, linenum, args[0]);
-                       goto out_err;
-               }
-
-               LIST_INIT(&rule->arg.map.key);
-               proxy->conf.args.ctx = ARGC_HRQ;
-               error = NULL;
-               if (!parse_logformat_string(args[cur_arg], proxy, &rule->arg.map.key, LOG_OPT_HTTP,
-                                           (proxy->cap & PR_CAP_FE) ? SMP_VAL_FE_HRQ_HDR : SMP_VAL_BE_HRQ_HDR, &error)) {
-                       ha_alert("parsing [%s:%d]: 'http-request %s': %s.\n",
-                                file, linenum, args[0], error);
-                       free(error);
-                       goto out_err;
-               }
-               free(proxy->conf.lfs_file);
-               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], "del-acl", 7) == 0) {
-               /* http-request del-acl(<reference (acl name)>) <key pattern> */
-               rule->action = ACT_HTTP_DEL_ACL;
-               /*
-                * '+ 8' for 'del-acl('
-                * '- 9' for 'del-acl(' + trailing ')'
-                */
-               rule->arg.map.ref = my_strndup(args[0] + 8, strlen(args[0]) - 9);
-
-               cur_arg = 1;
-
-               if (!*args[cur_arg] ||
-                   (*args[cur_arg+1] && strcmp(args[cur_arg+1], "if") != 0 && strcmp(args[cur_arg+1], "unless") != 0)) {
-                       ha_alert("parsing [%s:%d]: 'http-request %s' expects exactly 1 argument.\n",
-                                file, linenum, args[0]);
-                       goto out_err;
-               }
-
-               LIST_INIT(&rule->arg.map.key);
-               proxy->conf.args.ctx = ARGC_HRQ;
-               error = NULL;
-               if (!parse_logformat_string(args[cur_arg], proxy, &rule->arg.map.key, LOG_OPT_HTTP,
-                                           (proxy->cap & PR_CAP_FE) ? SMP_VAL_FE_HRQ_HDR : SMP_VAL_BE_HRQ_HDR, &error)) {
-                       ha_alert("parsing [%s:%d]: 'http-request %s': %s.\n",
-                                file, linenum, args[0], error);
-                       free(error);
-                       goto out_err;
-               }
-               free(proxy->conf.lfs_file);
-               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], "del-map", 7) == 0) {
-               /* http-request del-map(<reference (map name)>) <key pattern> */
-               rule->action = ACT_HTTP_DEL_MAP;
-               /*
-                * '+ 8' for 'del-map('
-                * '- 9' for 'del-map(' + trailing ')'
-                */
-               rule->arg.map.ref = my_strndup(args[0] + 8, strlen(args[0]) - 9);
-
-               cur_arg = 1;
-
-               if (!*args[cur_arg] ||
-                   (*args[cur_arg+1] && strcmp(args[cur_arg+1], "if") != 0 && strcmp(args[cur_arg+1], "unless") != 0)) {
-                       ha_alert("parsing [%s:%d]: 'http-request %s' expects exactly 1 argument.\n",
-                                file, linenum, args[0]);
-                       goto out_err;
-               }
-
-               LIST_INIT(&rule->arg.map.key);
-               proxy->conf.args.ctx = ARGC_HRQ;
-               error = NULL;
-               if (!parse_logformat_string(args[cur_arg], proxy, &rule->arg.map.key, LOG_OPT_HTTP,
-                                           (proxy->cap & PR_CAP_FE) ? SMP_VAL_FE_HRQ_HDR : SMP_VAL_BE_HRQ_HDR, &error)) {
-                       ha_alert("parsing [%s:%d]: 'http-request %s': %s.\n",
-                                file, linenum, args[0], error);
-                       free(error);
-                       goto out_err;
-               }
-               free(proxy->conf.lfs_file);
-               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], "set-map", 7) == 0) {
-               /* http-request set-map(<reference (map name)>) <key pattern> <value pattern> */
-               rule->action = ACT_HTTP_SET_MAP;
-               /*
-                * '+ 8' for 'set-map('
-                * '- 9' for 'set-map(' + trailing ')'
-                */
-               rule->arg.map.ref = my_strndup(args[0] + 8, strlen(args[0]) - 9);
-
-               cur_arg = 1;
-
-               if (!*args[cur_arg] || !*args[cur_arg+1] ||
-                   (*args[cur_arg+2] && strcmp(args[cur_arg+2], "if") != 0 && strcmp(args[cur_arg+2], "unless") != 0)) {
-                       ha_alert("parsing [%s:%d]: 'http-request %s' expects exactly 2 arguments.\n",
-                                file, linenum, args[0]);
-                       goto out_err;
-               }
-
-               LIST_INIT(&rule->arg.map.key);
-               LIST_INIT(&rule->arg.map.value);
-               proxy->conf.args.ctx = ARGC_HRQ;
-
-               /* key pattern */
-               error = NULL;
-               if (!parse_logformat_string(args[cur_arg], proxy, &rule->arg.map.key, LOG_OPT_HTTP,
-                                           (proxy->cap & PR_CAP_FE) ? SMP_VAL_FE_HRQ_HDR : SMP_VAL_BE_HRQ_HDR, &error)) {
-                       ha_alert("parsing [%s:%d]: 'http-request %s' key: %s.\n",
-                                file, linenum, args[0], error);
-                       free(error);
-                       goto out_err;
-               }
-
-               /* value pattern */
-               error = NULL;
-               if (!parse_logformat_string(args[cur_arg + 1], proxy, &rule->arg.map.value, LOG_OPT_HTTP,
-                                           (proxy->cap & PR_CAP_FE) ? SMP_VAL_FE_HRQ_HDR : SMP_VAL_BE_HRQ_HDR, &error)) {
-                       ha_alert("parsing [%s:%d]: 'http-request %s' pattern: %s.\n",
-                                file, linenum, args[0], error);
-                       free(error);
-                       goto out_err;
-               }
-               free(proxy->conf.lfs_file);
-               proxy->conf.lfs_file = strdup(proxy->conf.args.file);
-               proxy->conf.lfs_line = proxy->conf.args.line;
-
-               cur_arg += 2;
-       } else if (((custom = action_http_req_custom(args[0])) != NULL)) {
-               char *errmsg = NULL;
                cur_arg = 1;
                /* try in the module list */
-               rule->from = ACT_F_HTTP_REQ;
                rule->kw = custom;
                if (custom->parse(args, &cur_arg, proxy, rule, &errmsg) == ACT_RET_PRS_ERR) {
                        ha_alert("parsing [%s:%d] : error detected in %s '%s' while parsing 'http-request %s' rule : %s.\n",
@@ -532,12 +89,14 @@ struct act_rule *parse_http_req_cond(const char **args, const char *file, int li
                        free(errmsg);
                        goto out_err;
                }
-       } else {
+               else if (errmsg) {
+                       ha_warning("parsing [%s:%d] : %s.\n", file, linenum, errmsg);
+                       free(errmsg);
+               }
+       }
+       else {
                action_build_list(&http_req_keywords.list, &trash);
-               ha_alert("parsing [%s:%d]: 'http-request' expects 'allow', 'deny', 'auth', 'redirect', "
-                        "'tarpit', 'add-header', 'set-header', 'replace-header', 'replace-value', 'set-nice', "
-                        "'set-tos', 'set-mark', 'set-log-level', 'add-acl', 'del-acl', 'del-map', 'set-map', 'track-sc*'"
-                        "%s%s, but got '%s'%s.\n",
+               ha_alert("parsing [%s:%d]: 'http-request' expects %s%s, but got '%s'%s.\n",
                         file, linenum, *trash.area ? ", " : "", trash.area,
                         args[0], *args[0] ? "" : " (missing argument)");
                goto out_err;
@@ -556,8 +115,7 @@ struct act_rule *parse_http_req_cond(const char **args, const char *file, int li
                rule->cond = cond;
        }
        else if (*args[cur_arg]) {
-               ha_alert("parsing [%s:%d]: 'http-request %s' expects 'realm' for 'auth',"
-                        " 'deny_status' for 'deny', or"
+               ha_alert("parsing [%s:%d]: 'http-request %s' expects"
                         " either 'if' or 'unless' followed by a condition but found '%s'.\n",
                         file, linenum, args[0], args[cur_arg]);
                goto out_err;
@@ -575,410 +133,19 @@ struct act_rule *parse_http_res_cond(const char **args, const char *file, int li
        struct act_rule *rule;
        struct action_kw *custom = NULL;
        int cur_arg;
-       char *error;
 
        rule = calloc(1, sizeof(*rule));
        if (!rule) {
                ha_alert("parsing [%s:%d]: out of memory.\n", file, linenum);
                goto out_err;
        }
+       rule->from = ACT_F_HTTP_RES;
 
-       if (!strcmp(args[0], "allow")) {
-               rule->action = ACT_ACTION_ALLOW;
-               cur_arg = 1;
-       } else if (!strcmp(args[0], "deny")) {
-               rule->action = ACT_ACTION_DENY;
-               cur_arg = 1;
-       } else if (!strcmp(args[0], "set-nice")) {
-               rule->action = ACT_HTTP_SET_NICE;
-               cur_arg = 1;
-
-               if (!*args[cur_arg] ||
-                   (*args[cur_arg + 1] && strcmp(args[cur_arg + 1], "if") != 0 && strcmp(args[cur_arg + 1], "unless") != 0)) {
-                       ha_alert("parsing [%s:%d]: 'http-response %s' expects exactly 1 argument (integer value).\n",
-                                file, linenum, args[0]);
-                       goto out_err;
-               }
-               rule->arg.nice = atoi(args[cur_arg]);
-               if (rule->arg.nice < -1024)
-                       rule->arg.nice = -1024;
-               else if (rule->arg.nice > 1024)
-                       rule->arg.nice = 1024;
-               cur_arg++;
-       } else if (!strcmp(args[0], "set-tos")) {
-#ifdef IP_TOS
-               char *err;
-               rule->action = ACT_HTTP_SET_TOS;
-               cur_arg = 1;
-
-               if (!*args[cur_arg] ||
-                   (*args[cur_arg + 1] && strcmp(args[cur_arg + 1], "if") != 0 && strcmp(args[cur_arg + 1], "unless") != 0)) {
-                       ha_alert("parsing [%s:%d]: 'http-response %s' expects exactly 1 argument (integer/hex value).\n",
-                                file, linenum, args[0]);
-                       goto out_err;
-               }
-
-               rule->arg.tos = strtol(args[cur_arg], &err, 0);
-               if (err && *err != '\0') {
-                       ha_alert("parsing [%s:%d]: invalid character starting at '%s' in 'http-response %s' (integer/hex value expected).\n",
-                                file, linenum, err, args[0]);
-                       goto out_err;
-               }
-               cur_arg++;
-#else
-               ha_alert("parsing [%s:%d]: 'http-response %s' is not supported on this platform (IP_TOS undefined).\n", file, linenum, args[0]);
-               goto out_err;
-#endif
-       } else if (!strcmp(args[0], "set-mark")) {
-#ifdef SO_MARK
-               char *err;
-               rule->action = ACT_HTTP_SET_MARK;
-               cur_arg = 1;
-
-               if (!*args[cur_arg] ||
-                   (*args[cur_arg + 1] && strcmp(args[cur_arg + 1], "if") != 0 && strcmp(args[cur_arg + 1], "unless") != 0)) {
-                       ha_alert("parsing [%s:%d]: 'http-response %s' expects exactly 1 argument (integer/hex value).\n",
-                                file, linenum, args[0]);
-                       goto out_err;
-               }
-
-               rule->arg.mark = strtoul(args[cur_arg], &err, 0);
-               if (err && *err != '\0') {
-                       ha_alert("parsing [%s:%d]: invalid character starting at '%s' in 'http-response %s' (integer/hex value expected).\n",
-                                file, linenum, err, args[0]);
-                       goto out_err;
-               }
-               cur_arg++;
-               global.last_checks |= LSTCHK_NETADM;
-#else
-               ha_alert("parsing [%s:%d]: 'http-response %s' is not supported on this platform (SO_MARK undefined).\n", file, linenum, args[0]);
-               goto out_err;
-#endif
-       } else if (!strcmp(args[0], "set-log-level")) {
-               rule->action = ACT_HTTP_SET_LOGL;
-               cur_arg = 1;
-
-               if (!*args[cur_arg] ||
-                   (*args[cur_arg + 1] && strcmp(args[cur_arg + 1], "if") != 0 && strcmp(args[cur_arg + 1], "unless") != 0)) {
-               bad_log_level:
-                       ha_alert("parsing [%s:%d]: 'http-response %s' expects exactly 1 argument (log level name or 'silent').\n",
-                                file, linenum, args[0]);
-                       goto out_err;
-               }
-               if (strcmp(args[cur_arg], "silent") == 0)
-                       rule->arg.loglevel = -1;
-               else if ((rule->arg.loglevel = get_log_level(args[cur_arg]) + 1) == 0)
-                       goto bad_log_level;
-               cur_arg++;
-       } else if (strcmp(args[0], "add-header") == 0 || strcmp(args[0], "set-header") == 0) {
-               rule->action = *args[0] == 'a' ? ACT_HTTP_ADD_HDR : ACT_HTTP_SET_HDR;
-               cur_arg = 1;
-
-               if (!*args[cur_arg] || !*args[cur_arg+1] ||
-                   (*args[cur_arg+2] && strcmp(args[cur_arg+2], "if") != 0 && strcmp(args[cur_arg+2], "unless") != 0)) {
-                       ha_alert("parsing [%s:%d]: 'http-response %s' expects exactly 2 arguments.\n",
-                                file, linenum, args[0]);
-                       goto out_err;
-               }
-
-               rule->arg.hdr_add.name = strdup(args[cur_arg]);
-               rule->arg.hdr_add.name_len = strlen(rule->arg.hdr_add.name);
-               LIST_INIT(&rule->arg.hdr_add.fmt);
-
-               proxy->conf.args.ctx = ARGC_HRS;
-               error = NULL;
-               if (!parse_logformat_string(args[cur_arg + 1], proxy, &rule->arg.hdr_add.fmt, LOG_OPT_HTTP,
-                                           (proxy->cap & PR_CAP_BE) ? SMP_VAL_BE_HRS_HDR : SMP_VAL_FE_HRS_HDR, &error)) {
-                       ha_alert("parsing [%s:%d]: 'http-response %s': %s.\n",
-                                file, linenum, args[0], error);
-                       free(error);
-                       goto out_err;
-               }
-               free(proxy->conf.lfs_file);
-               proxy->conf.lfs_file = strdup(proxy->conf.args.file);
-               proxy->conf.lfs_line = proxy->conf.args.line;
-               cur_arg += 2;
-       } else if (strcmp(args[0], "replace-header") == 0 || strcmp(args[0], "replace-value") == 0) {
-               rule->action = args[0][8] == 'h' ? ACT_HTTP_REPLACE_HDR : ACT_HTTP_REPLACE_VAL;
-               cur_arg = 1;
-
-               if (!*args[cur_arg] || !*args[cur_arg+1] || !*args[cur_arg+2] ||
-                   (*args[cur_arg+3] && strcmp(args[cur_arg+3], "if") != 0 && strcmp(args[cur_arg+3], "unless") != 0)) {
-                       ha_alert("parsing [%s:%d]: 'http-response %s' expects exactly 3 arguments.\n",
-                                file, linenum, args[0]);
-                       goto out_err;
-               }
-
-               rule->arg.hdr_add.name = strdup(args[cur_arg]);
-               rule->arg.hdr_add.name_len = strlen(rule->arg.hdr_add.name);
-               LIST_INIT(&rule->arg.hdr_add.fmt);
-
-               error = NULL;
-               if (!(rule->arg.hdr_add.re = regex_comp(args[cur_arg + 1], 1, 1, &error))) {
-                       ha_alert("parsing [%s:%d] : '%s' : %s.\n", file, linenum,
-                                args[cur_arg + 1], error);
-                       free(error);
-                       goto out_err;
-               }
-
-               proxy->conf.args.ctx = ARGC_HRQ;
-               error = NULL;
-               if (!parse_logformat_string(args[cur_arg + 2], proxy, &rule->arg.hdr_add.fmt, LOG_OPT_HTTP,
-                                           (proxy->cap & PR_CAP_BE) ? SMP_VAL_BE_HRS_HDR : SMP_VAL_FE_HRS_HDR, &error)) {
-                       ha_alert("parsing [%s:%d]: 'http-response %s': %s.\n",
-                                file, linenum, args[0], error);
-                       free(error);
-                       goto out_err;
-               }
-
-               free(proxy->conf.lfs_file);
-               proxy->conf.lfs_file = strdup(proxy->conf.args.file);
-               proxy->conf.lfs_line = proxy->conf.args.line;
-               cur_arg += 3;
-       } else if (strcmp(args[0], "del-header") == 0) {
-               rule->action = ACT_HTTP_DEL_HDR;
-               cur_arg = 1;
-
-               if (!*args[cur_arg] ||
-                   (*args[cur_arg+1] && strcmp(args[cur_arg+1], "if") != 0 && strcmp(args[cur_arg+1], "unless") != 0)) {
-                       ha_alert("parsing [%s:%d]: 'http-response %s' expects exactly 1 argument.\n",
-                                file, linenum, args[0]);
-                       goto out_err;
-               }
-
-               rule->arg.hdr_add.name = strdup(args[cur_arg]);
-               rule->arg.hdr_add.name_len = strlen(rule->arg.hdr_add.name);
-
-               proxy->conf.args.ctx = ARGC_HRS;
-               free(proxy->conf.lfs_file);
-               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], "add-acl", 7) == 0) {
-               /* http-request add-acl(<reference (acl name)>) <key pattern> */
-               rule->action = ACT_HTTP_ADD_ACL;
-               /*
-                * '+ 8' for 'add-acl('
-                * '- 9' for 'add-acl(' + trailing ')'
-                */
-               rule->arg.map.ref = my_strndup(args[0] + 8, strlen(args[0]) - 9);
-
-               cur_arg = 1;
-
-               if (!*args[cur_arg] ||
-                   (*args[cur_arg+1] && strcmp(args[cur_arg+1], "if") != 0 && strcmp(args[cur_arg+1], "unless") != 0)) {
-                       ha_alert("parsing [%s:%d]: 'http-response %s' expects exactly 1 argument.\n",
-                                file, linenum, args[0]);
-                       goto out_err;
-               }
-
-               LIST_INIT(&rule->arg.map.key);
-               proxy->conf.args.ctx = ARGC_HRS;
-               error = NULL;
-               if (!parse_logformat_string(args[cur_arg], proxy, &rule->arg.map.key, LOG_OPT_HTTP,
-                                           (proxy->cap & PR_CAP_BE) ? SMP_VAL_BE_HRS_HDR : SMP_VAL_FE_HRS_HDR, &error)) {
-                       ha_alert("parsing [%s:%d]: 'http-response %s': %s.\n",
-                                file, linenum, args[0], error);
-                       free(error);
-                       goto out_err;
-               }
-               free(proxy->conf.lfs_file);
-               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], "del-acl", 7) == 0) {
-               /* http-response del-acl(<reference (acl name)>) <key pattern> */
-               rule->action = ACT_HTTP_DEL_ACL;
-               /*
-                * '+ 8' for 'del-acl('
-                * '- 9' for 'del-acl(' + trailing ')'
-                */
-               rule->arg.map.ref = my_strndup(args[0] + 8, strlen(args[0]) - 9);
-
-               cur_arg = 1;
-
-               if (!*args[cur_arg] ||
-                   (*args[cur_arg+1] && strcmp(args[cur_arg+1], "if") != 0 && strcmp(args[cur_arg+1], "unless") != 0)) {
-                       ha_alert("parsing [%s:%d]: 'http-response %s' expects exactly 1 argument.\n",
-                                file, linenum, args[0]);
-                       goto out_err;
-               }
-
-               LIST_INIT(&rule->arg.map.key);
-               proxy->conf.args.ctx = ARGC_HRS;
-               error = NULL;
-               if (!parse_logformat_string(args[cur_arg], proxy, &rule->arg.map.key, LOG_OPT_HTTP,
-                                           (proxy->cap & PR_CAP_BE) ? SMP_VAL_BE_HRS_HDR : SMP_VAL_FE_HRS_HDR, &error)) {
-                       ha_alert("parsing [%s:%d]: 'http-response %s': %s.\n",
-                                file, linenum, args[0], error);
-                       free(error);
-                       goto out_err;
-               }
-               free(proxy->conf.lfs_file);
-               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], "del-map", 7) == 0) {
-               /* http-response del-map(<reference (map name)>) <key pattern> */
-               rule->action = ACT_HTTP_DEL_MAP;
-               /*
-                * '+ 8' for 'del-map('
-                * '- 9' for 'del-map(' + trailing ')'
-                */
-               rule->arg.map.ref = my_strndup(args[0] + 8, strlen(args[0]) - 9);
-
-               cur_arg = 1;
-
-               if (!*args[cur_arg] ||
-                   (*args[cur_arg+1] && strcmp(args[cur_arg+1], "if") != 0 && strcmp(args[cur_arg+1], "unless") != 0)) {
-                       ha_alert("parsing [%s:%d]: 'http-response %s' expects exactly 1 argument.\n",
-                                file, linenum, args[0]);
-                       goto out_err;
-               }
-
-               LIST_INIT(&rule->arg.map.key);
-               proxy->conf.args.ctx = ARGC_HRS;
-               error = NULL;
-               if (!parse_logformat_string(args[cur_arg], proxy, &rule->arg.map.key, LOG_OPT_HTTP,
-                                           (proxy->cap & PR_CAP_BE) ? SMP_VAL_BE_HRS_HDR : SMP_VAL_FE_HRS_HDR, &error)) {
-                       ha_alert("parsing [%s:%d]: 'http-response %s' %s.\n",
-                                file, linenum, args[0], error);
-                       free(error);
-                       goto out_err;
-               }
-               free(proxy->conf.lfs_file);
-               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], "set-map", 7) == 0) {
-               /* http-response set-map(<reference (map name)>) <key pattern> <value pattern> */
-               rule->action = ACT_HTTP_SET_MAP;
-               /*
-                * '+ 8' for 'set-map('
-                * '- 9' for 'set-map(' + trailing ')'
-                */
-               rule->arg.map.ref = my_strndup(args[0] + 8, strlen(args[0]) - 9);
-
-               cur_arg = 1;
-
-               if (!*args[cur_arg] || !*args[cur_arg+1] ||
-                   (*args[cur_arg+2] && strcmp(args[cur_arg+2], "if") != 0 && strcmp(args[cur_arg+2], "unless") != 0)) {
-                       ha_alert("parsing [%s:%d]: 'http-response %s' expects exactly 2 arguments.\n",
-                                file, linenum, args[0]);
-                       goto out_err;
-               }
-
-               LIST_INIT(&rule->arg.map.key);
-               LIST_INIT(&rule->arg.map.value);
-
-               proxy->conf.args.ctx = ARGC_HRS;
-
-               /* key pattern */
-               error = NULL;
-               if (!parse_logformat_string(args[cur_arg], proxy, &rule->arg.map.key, LOG_OPT_HTTP,
-                                           (proxy->cap & PR_CAP_BE) ? SMP_VAL_BE_HRS_HDR : SMP_VAL_FE_HRS_HDR, &error)) {
-                       ha_alert("parsing [%s:%d]: 'http-response %s' name: %s.\n",
-                                file, linenum, args[0], error);
-                       free(error);
-                       goto out_err;
-               }
-
-               /* value pattern */
-               error = NULL;
-               if (!parse_logformat_string(args[cur_arg + 1], proxy, &rule->arg.map.value, LOG_OPT_HTTP,
-                                           (proxy->cap & PR_CAP_BE) ? SMP_VAL_BE_HRS_HDR : SMP_VAL_FE_HRS_HDR, &error)) {
-                       ha_alert("parsing [%s:%d]: 'http-response %s' value: %s.\n",
-                                file, linenum, args[0], error);
-                       free(error);
-                       goto out_err;
-               }
-
-               free(proxy->conf.lfs_file);
-               proxy->conf.lfs_file = strdup(proxy->conf.args.file);
-               proxy->conf.lfs_line = proxy->conf.args.line;
-
-               cur_arg += 2;
-       } else if (strcmp(args[0], "redirect") == 0) {
-               struct redirect_rule *redir;
+       if (((custom = action_http_res_custom(args[0])) != NULL)) {
                char *errmsg = NULL;
 
-               if ((redir = http_parse_redirect_rule(file, linenum, proxy, (const char **)args + 1, &errmsg, 1, 1)) == NULL) {
-                       ha_alert("parsing [%s:%d] : error detected in %s '%s' while parsing 'http-response %s' rule : %s.\n",
-                                file, linenum, proxy_type_str(proxy), proxy->id, args[0], errmsg);
-                       goto out_err;
-               }
-
-               /* this redirect rule might already contain a parsed condition which
-                * we'll pass to the http-request rule.
-                */
-               rule->action = ACT_HTTP_REDIR;
-               rule->arg.redir = redir;
-               rule->cond = redir->cond;
-               redir->cond = NULL;
-               cur_arg = 2;
-               return rule;
-       } else if (strncmp(args[0], "track-sc", 8) == 0) {
-               struct sample_expr *expr;
-               unsigned int where;
-               char *err = NULL;
-               unsigned int tsc_num;
-               const char *tsc_num_str;
-
-               cur_arg = 1;
-               proxy->conf.args.ctx = ARGC_TRK;
-
-               tsc_num_str = &args[0][8];
-               if (cfg_parse_track_sc_num(&tsc_num, tsc_num_str, tsc_num_str + strlen(tsc_num_str), &err) == -1) {
-                       ha_alert("parsing [%s:%d] : error detected in %s '%s' while parsing 'http-response %s' rule : %s.\n",
-                                file, linenum, proxy_type_str(proxy), proxy->id, args[0], err);
-                       free(err);
-                       goto out_err;
-               }
-
-               expr = sample_parse_expr((char **)args, &cur_arg, file, linenum, &err, &proxy->conf.args);
-               if (!expr) {
-                       ha_alert("parsing [%s:%d] : error detected in %s '%s' while parsing 'http-response %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_HRS_HDR;
-               if (proxy->cap & PR_CAP_BE)
-                       where |= SMP_VAL_BE_HRS_HDR;
-
-               if (!(expr->fetch->val & where)) {
-                       ha_alert("parsing [%s:%d] : error detected in %s '%s' while parsing 'http-response %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]) {
-                               ha_alert("parsing [%s:%d] : error detected in %s '%s' while parsing 'http-response %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->arg.trk_ctr.table.n = strdup(args[cur_arg]);
-                       cur_arg++;
-               }
-               rule->arg.trk_ctr.expr = expr;
-               rule->action = ACT_ACTION_TRK_SC0 + tsc_num;
-               rule->check_ptr = check_trk_action;
-       } else if (((custom = action_http_res_custom(args[0])) != NULL)) {
-               char *errmsg = NULL;
                cur_arg = 1;
                /* try in the module list */
-               rule->from = ACT_F_HTTP_RES;
                rule->kw = custom;
                if (custom->parse(args, &cur_arg, proxy, rule, &errmsg) == ACT_RET_PRS_ERR) {
                        ha_alert("parsing [%s:%d] : error detected in %s '%s' while parsing 'http-response %s' rule : %s.\n",
@@ -986,12 +153,14 @@ struct act_rule *parse_http_res_cond(const char **args, const char *file, int li
                        free(errmsg);
                        goto out_err;
                }
-       } else {
+               else if (errmsg) {
+                       ha_warning("parsing [%s:%d] : %s.\n", file, linenum, errmsg);
+                       free(errmsg);
+               }
+       }
+       else {
                action_build_list(&http_res_keywords.list, &trash);
-               ha_alert("parsing [%s:%d]: 'http-response' expects 'allow', 'deny', 'redirect', "
-                        "'add-header', 'del-header', 'set-header', 'replace-header', 'replace-value', 'set-nice', "
-                        "'set-tos', 'set-mark', 'set-log-level', 'add-acl', 'del-acl', 'del-map', 'set-map', 'track-sc*'"
-                        "%s%s, but got '%s'%s.\n",
+               ha_alert("parsing [%s:%d]: 'http-response' expects %s%s, but got '%s'%s.\n",
                         file, linenum, *trash.area ? ", " : "", trash.area,
                         args[0], *args[0] ? "" : " (missing argument)");
                goto out_err;