MEDIUM: http: ACL and MAP updates through http-(request|response) rules
This patch allows manipulation of ACL and MAP content thanks to any
information available in a session: source IP address, HTTP request or
response header, etc...
It's an update "on the fly" of the content of the map/acls. This means
it does not resist to reload or restart of HAProxy.
diff --git a/doc/configuration.txt b/doc/configuration.txt
index 6bb5ffd..84c4fb5 100644
--- a/doc/configuration.txt
+++ b/doc/configuration.txt
@@ -2848,7 +2848,12 @@
http-request { allow | deny | tarpit | auth [realm <realm>] | redirect <rule> |
add-header <name> <fmt> | set-header <name> <fmt> |
del-header <name> | set-nice <nice> | set-log-level <level> |
- set-tos <tos> | set-mark <mark> }
+ set-tos <tos> | set-mark <mark> |
+ add-acl(<file name>) <key fmt> |
+ del-acl(<file name>) <key fmt> |
+ del-map(<file name>) <key fmt> |
+ set-map(<file name>) <key fmt> <value fmt>
+ }
[ { if | unless } <condition> ]
Access control for Layer 7 requests
@@ -2944,6 +2949,39 @@
downloads). This works on Linux kernels 2.6.32 and above and requires
admin privileges.
+ - "add-acl" is used to add a new entry into an ACL. The ACL must be loaded
+ from a file (even a dummy empty file). The file name of the ACL to be
+ updated is passed between parentheses. It takes one argument: <key fmt>,
+ which follows log-format rules, to collect content of the new entry. It
+ performs a lookup in the ACL before insertion, to avoid duplicated (or
+ more) values. This lookup is done by a linear search and can be expensive
+ with large lists! It is the equivalent of the "add acl" command from the
+ stats socket, but can be triggered by an HTTP request.
+
+ - "del-acl" is used to delete an entry from an ACL. The ACL must be loaded
+ from a file (even a dummy empty file). The file name of the ACL to be
+ updated is passed between parentheses. It takes one argument: <key fmt>,
+ which follows log-format rules, to collect content of the entry to delete.
+ It is the equivalent of the "del acl" command from the stats socket, but
+ can be triggered by an HTTP request.
+
+ - "del-map" is used to delete an entry from a MAP. The MAP must be loaded
+ from a file (even a dummy empty file). The file name of the MAP to be
+ updated is passed between parentheses. It takes one argument: <key fmt>,
+ which follows log-format rules, to collect content of the entry to delete.
+ It takes one argument: "file name" It is the equivalent of the "del map"
+ command from the stats socket, but can be triggered by an HTTP request.
+
+ - "set-map" is used to add a new entry into a MAP. The MAP must be loaded
+ from a file (even a dummy empty file). The file name of the MAP to be
+ updated is passed between parentheses. It takes 2 arguments: <key fmt>,
+ which follows log-format rules, used to collect MAP key, and <value fmt>,
+ which follows log-format rules, used to collect content for the new entry.
+ It performs a lookup in the MAP before insertion, to avoid duplicated (or
+ more) values. This lookup is done by a linear search and can be expensive
+ with large lists! It is the equivalent of the "set map" command from the
+ stats socket, but can be triggered by an HTTP request.
+
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
@@ -2976,12 +3014,37 @@
http-request set-header X-SSL-Client-NotBefore %{+Q}[ssl_c_notbefore]
http-request set-header X-SSL-Client-NotAfter %{+Q}[ssl_c_notafter]
+ Example:
+ acl key req.hdr(X-Add-Acl-Key) -m found
+ acl add path /addacl
+ acl del path /delacl
+
+ acl myhost hdr(Host) -f myhost.lst
+
+ http-request add-acl(myhost.lst) %[req.hdr(X-Add-Acl-Key)] if key add
+ http-request del-acl(myhost.lst) %[req.hdr(X-Add-Acl-Key)] if key del
+
+ Example:
+ acl value req.hdr(X-Value) -m found
+ acl setmap path /setmap
+ acl delmap path /delmap
+
+ use_backend bk_appli if { hdr(Host),map_str(map.lst) -m found }
+
+ http-request set-map(map.lst) %[src] %[req.hdr(X-Value)] if setmap value
+ http-request del-map(map.lst) %[src] if delmap
+
See also : "stats http-request", section 3.4 about userlists and section 7
about ACL usage.
http-response { allow | deny | add-header <name> <fmt> | set-nice <nice> |
set-header <name> <fmt> | del-header <name> |
- set-log-level <level> | set-mark <mark> | set-tos <tos> }
+ set-log-level <level> | set-mark <mark> | set-tos <tos> |
+ add-acl(<file name>) <key fmt> |
+ del-acl(<file name>) <key fmt> |
+ del-map(<file name>) <key fmt> |
+ set-map(<file name>) <key fmt> <value fmt>
+ }
[ { if | unless } <condition> ]
Access control for Layer 7 responses
@@ -3054,6 +3117,39 @@
downloads). This works on Linux kernels 2.6.32 and above and requires
admin privileges.
+ - "add-acl" is used to add a new entry into an ACL. The ACL must be loaded
+ from a file (even a dummy empty file). The file name of the ACL to be
+ updated is passed between parentheses. It takes one argument: <key fmt>,
+ which follows log-format rules, to collect content of the new entry. It
+ performs a lookup in the ACL before insertion, to avoid duplicated (or
+ more) values. This lookup is done by a linear search and can be expensive
+ with large lists! It is the equivalent of the "add acl" command from the
+ stats socket, but can be triggered by an HTTP response.
+
+ - "del-acl" is used to delete an entry from an ACL. The ACL must be loaded
+ from a file (even a dummy empty file). The file name of the ACL to be
+ updated is passed between parentheses. It takes one argument: <key fmt>,
+ which follows log-format rules, to collect content of the entry to delete.
+ It is the equivalent of the "del acl" command from the stats socket, but
+ can be triggered by an HTTP response.
+
+ - "del-map" is used to delete an entry from a MAP. The MAP must be loaded
+ from a file (even a dummy empty file). The file name of the MAP to be
+ updated is passed between parentheses. It takes one argument: <key fmt>,
+ which follows log-format rules, to collect content of the entry to delete.
+ It takes one argument: "file name" It is the equivalent of the "del map"
+ command from the stats socket, but can be triggered by an HTTP response.
+
+ - "set-map" is used to add a new entry into a MAP. The MAP must be loaded
+ from a file (even a dummy empty file). The file name of the MAP to be
+ updated is passed between parentheses. It takes 2 arguments: <key fmt>,
+ which follows log-format rules, used to collect MAP key, and <value fmt>,
+ which follows log-format rules, used to collect content for the new entry.
+ It performs a lookup in the MAP before insertion, to avoid duplicated (or
+ more) values. This lookup is done by a linear search and can be expensive
+ with large lists! It is the equivalent of the "set map" command from the
+ stats socket, but can be triggered by an HTTP response.
+
There is no limit to the number of http-response statements per instance.
It is important to know that http-response rules are processed very early in
@@ -3061,6 +3157,22 @@
added by "add-header"/"set-header" are visible by almost all further ACL
rules.
+ Example:
+ acl key_acl res.hdr(X-Acl-Key) -m found
+
+ acl myhost hdr(Host) -f myhost.lst
+
+ http-response add-acl(myhost.lst) %[res.hdr(X-Acl-Key)] if key_acl
+ http-response del-acl(myhost.lst) %[res.hdr(X-Acl-Key)] if key_acl
+
+ Example:
+ acl value res.hdr(X-Value) -m found
+
+ use_backend bk_appli if { hdr(Host),map_str(map.lst) -m found }
+
+ http-response set-map(map.lst) %[src] %[res.hdr(X-Value)] if value
+ http-response del-map(map.lst) %[src] if ! value
+
See also : "http-request", section 3.4 about userlists and section 7 about
ACL usage.
diff --git a/include/types/proto_http.h b/include/types/proto_http.h
index d937ce1..f084ecd 100644
--- a/include/types/proto_http.h
+++ b/include/types/proto_http.h
@@ -252,6 +252,10 @@
HTTP_REQ_ACT_SET_LOGL,
HTTP_REQ_ACT_SET_TOS,
HTTP_REQ_ACT_SET_MARK,
+ HTTP_REQ_ACT_ADD_ACL,
+ HTTP_REQ_ACT_DEL_ACL,
+ HTTP_REQ_ACT_DEL_MAP,
+ HTTP_REQ_ACT_SET_MAP,
HTTP_REQ_ACT_MAX /* must always be last */
};
@@ -267,6 +271,10 @@
HTTP_RES_ACT_SET_LOGL,
HTTP_RES_ACT_SET_TOS,
HTTP_RES_ACT_SET_MARK,
+ HTTP_RES_ACT_ADD_ACL,
+ HTTP_RES_ACT_DEL_ACL,
+ HTTP_RES_ACT_DEL_MAP,
+ HTTP_RES_ACT_SET_MAP,
HTTP_RES_ACT_MAX /* must always be last */
};
@@ -404,6 +412,11 @@
int loglevel; /* log-level value for HTTP_REQ_ACT_SET_LOGL */
int tos; /* tos value for HTTP_REQ_ACT_SET_TOS */
int mark; /* nfmark value for HTTP_REQ_ACT_SET_MARK */
+ struct {
+ char *ref; /* MAP or ACL file name to update */
+ struct list key; /* pattern to retrieve MAP or ACL key */
+ struct list value; /* pattern to retrieve MAP value */
+ } map;
} arg; /* arguments used by some actions */
};
@@ -421,6 +434,11 @@
int loglevel; /* log-level value for HTTP_RES_ACT_SET_LOGL */
int tos; /* tos value for HTTP_RES_ACT_SET_TOS */
int mark; /* nfmark value for HTTP_RES_ACT_SET_MARK */
+ struct {
+ char *ref; /* MAP or ACL file name to update */
+ struct list key; /* pattern to retrieve MAP or ACL key */
+ struct list value; /* pattern to retrieve MAP value */
+ } map;
} arg; /* arguments used by some actions */
};
diff --git a/src/proto_http.c b/src/proto_http.c
index 3675b85..e9004f8 100644
--- a/src/proto_http.c
+++ b/src/proto_http.c
@@ -64,6 +64,7 @@
#include <proto/session.h>
#include <proto/stream_interface.h>
#include <proto/task.h>
+#include <proto/pattern.h>
const char HTTP_100[] =
"HTTP/1.1 100 Continue\r\n\r\n";
@@ -3204,6 +3205,90 @@
trash.len += build_logline(s, trash.str + trash.len, trash.size - trash.len, &rule->arg.hdr_add.fmt);
http_header_add_tail2(&txn->req, &txn->hdr_idx, trash.str, trash.len);
break;
+
+ case HTTP_REQ_ACT_DEL_ACL:
+ case HTTP_REQ_ACT_DEL_MAP: {
+ struct pat_ref *ref;
+ char *key;
+ int len;
+
+ /* collect reference */
+ ref = pat_ref_lookup(rule->arg.map.ref);
+ if (!ref)
+ continue;
+
+ /* collect key */
+ len = build_logline(s, trash.str, trash.size, &rule->arg.map.key);
+ key = trash.str;
+ key[len] = '\0';
+
+ /* perform update */
+ /* returned code: 1=ok, 0=ko */
+ pat_ref_delete(ref, key);
+
+ break;
+ }
+
+ case HTTP_REQ_ACT_ADD_ACL: {
+ struct pat_ref *ref;
+ char *key;
+ struct chunk *trash_key;
+ int len;
+
+ trash_key = get_trash_chunk();
+
+ /* collect reference */
+ ref = pat_ref_lookup(rule->arg.map.ref);
+ if (!ref)
+ continue;
+
+ /* collect key */
+ len = build_logline(s, trash_key->str, trash_key->size, &rule->arg.map.key);
+ key = trash_key->str;
+ key[len] = '\0';
+
+ /* perform update */
+ /* add entry only if it does not already exist */
+ if (pat_ref_find_elt(ref, key) == NULL)
+ pat_ref_add(ref, key, NULL, NULL);
+
+ break;
+ }
+
+ case HTTP_REQ_ACT_SET_MAP: {
+ struct pat_ref *ref;
+ char *key, *value;
+ struct chunk *trash_key, *trash_value;
+ int len;
+
+ trash_key = get_trash_chunk();
+ trash_value = get_trash_chunk();
+
+ /* collect reference */
+ ref = pat_ref_lookup(rule->arg.map.ref);
+ if (!ref)
+ continue;
+
+ /* collect key */
+ len = build_logline(s, trash_key->str, trash_key->size, &rule->arg.map.key);
+ key = trash_key->str;
+ key[len] = '\0';
+
+ /* collect value */
+ len = build_logline(s, trash_value->str, trash_value->size, &rule->arg.map.value);
+ value = trash_value->str;
+ value[len] = '\0';
+
+ /* perform update */
+ if (pat_ref_find_elt(ref, key) != NULL)
+ /* update entry if it exists */
+ pat_ref_set(ref, key, value, NULL);
+ else
+ /* insert a new entry */
+ pat_ref_add(ref, key, value, NULL);
+
+ break;
+ }
}
}
@@ -3293,6 +3378,90 @@
trash.len += build_logline(s, trash.str + trash.len, trash.size - trash.len, &rule->arg.hdr_add.fmt);
http_header_add_tail2(&txn->rsp, &txn->hdr_idx, trash.str, trash.len);
break;
+
+ case HTTP_RES_ACT_DEL_ACL:
+ case HTTP_RES_ACT_DEL_MAP: {
+ struct pat_ref *ref;
+ char *key;
+ int len;
+
+ /* collect reference */
+ ref = pat_ref_lookup(rule->arg.map.ref);
+ if (!ref)
+ continue;
+
+ /* collect key */
+ len = build_logline(s, trash.str, trash.size, &rule->arg.map.key);
+ key = trash.str;
+ key[len] = '\0';
+
+ /* perform update */
+ /* returned code: 1=ok, 0=ko */
+ pat_ref_delete(ref, key);
+
+ break;
+ }
+
+ case HTTP_RES_ACT_ADD_ACL: {
+ struct pat_ref *ref;
+ char *key;
+ struct chunk *trash_key;
+ int len;
+
+ trash_key = get_trash_chunk();
+
+ /* collect reference */
+ ref = pat_ref_lookup(rule->arg.map.ref);
+ if (!ref)
+ continue;
+
+ /* collect key */
+ len = build_logline(s, trash_key->str, trash_key->size, &rule->arg.map.key);
+ key = trash_key->str;
+ key[len] = '\0';
+
+ /* perform update */
+ /* check if the entry already exists */
+ if (pat_ref_find_elt(ref, key) == NULL)
+ pat_ref_add(ref, key, NULL, NULL);
+
+ break;
+ }
+
+ case HTTP_RES_ACT_SET_MAP: {
+ struct pat_ref *ref;
+ char *key, *value;
+ struct chunk *trash_key, *trash_value;
+ int len;
+
+ trash_key = get_trash_chunk();
+ trash_value = get_trash_chunk();
+
+ /* collect reference */
+ ref = pat_ref_lookup(rule->arg.map.ref);
+ if (!ref)
+ continue;
+
+ /* collect key */
+ len = build_logline(s, trash_key->str, trash_key->size, &rule->arg.map.key);
+ key = trash_key->str;
+ key[len] = '\0';
+
+ /* collect value */
+ len = build_logline(s, trash_value->str, trash_value->size, &rule->arg.map.value);
+ value = trash_value->str;
+ value[len] = '\0';
+
+ /* perform update */
+ if (pat_ref_find_elt(ref, key) != NULL)
+ /* update entry if it exists */
+ pat_ref_set(ref, key, value, NULL);
+ else
+ /* insert a new entry */
+ pat_ref_add(ref, key, value, NULL);
+
+ break;
+ }
}
}
@@ -8652,8 +8821,125 @@
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 = HTTP_REQ_ACT_ADD_ACL;
+ /*
+ * '+ 8' for 'add-acl('
+ * '- 9' for 'add-acl(' + trailing ')'
+ */
+ rule->arg.map.ref = 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)) {
+ 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;
+ 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,
+ file, linenum);
+ 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 = HTTP_REQ_ACT_DEL_ACL;
+ /*
+ * '+ 8' for 'del-acl('
+ * '- 9' for 'del-acl(' + trailing ')'
+ */
+ rule->arg.map.ref = 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)) {
+ 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;
+ 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,
+ file, linenum);
+ 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 = HTTP_REQ_ACT_DEL_MAP;
+ /*
+ * '+ 8' for 'del-map('
+ * '- 9' for 'del-map(' + trailing ')'
+ */
+ rule->arg.map.ref = 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)) {
+ 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;
+ 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,
+ file, linenum);
+ 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 = HTTP_REQ_ACT_SET_MAP;
+ /*
+ * '+ 8' for 'set-map('
+ * '- 9' for 'set-map(' + trailing ')'
+ */
+ rule->arg.map.ref = 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)) {
+ 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 */
+ 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,
+ file, linenum);
+
+ /* value pattern */
+ 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,
+ file, linenum);
+ 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 {
- Alert("parsing [%s:%d]: 'http-request' expects 'allow', 'deny', 'auth', 'redirect', 'tarpit', 'add-header', 'set-header', 'set-nice', 'set-tos', 'set-mark', 'set-log-level', but got '%s'%s.\n",
+ Alert("parsing [%s:%d]: 'http-request' expects 'allow', 'deny', 'auth', 'redirect', 'tarpit', 'add-header', 'set-header', 'set-nice', 'set-tos', 'set-mark', 'set-log-level', 'add-acl', 'del-acl', 'del-map', 'set-map', but got '%s'%s.\n",
file, linenum, args[0], *args[0] ? "" : " (missing argument)");
goto out_err;
}
@@ -8819,13 +9105,133 @@
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 = HTTP_RES_ACT_ADD_ACL;
+ /*
+ * '+ 8' for 'add-acl('
+ * '- 9' for 'add-acl(' + trailing ')'
+ */
+ rule->arg.map.ref = 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)) {
+ 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;
+ 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,
+ file, linenum);
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 = HTTP_RES_ACT_DEL_ACL;
+ /*
+ * '+ 8' for 'del-acl('
+ * '- 9' for 'del-acl(' + trailing ')'
+ */
+ rule->arg.map.ref = 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)) {
+ 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;
+ 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,
+ file, linenum);
+ 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 = HTTP_RES_ACT_DEL_MAP;
+ /*
+ * '+ 8' for 'del-map('
+ * '- 9' for 'del-map(' + trailing ')'
+ */
+ rule->arg.map.ref = 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)) {
+ 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;
+ 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,
+ file, linenum);
+ 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 = HTTP_RES_ACT_SET_MAP;
+ /*
+ * '+ 8' for 'set-map('
+ * '- 9' for 'set-map(' + trailing ')'
+ */
+ rule->arg.map.ref = 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)) {
+ 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 */
+ 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,
+ file, linenum);
+
+ /* value pattern */
+ 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,
+ file, linenum);
+
+ 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 {
- Alert("parsing [%s:%d]: 'http-response' expects 'allow', 'deny', 'redirect', 'add-header', 'set-header', 'set-nice', 'set-tos', 'set-mark', 'set-log-level', but got '%s'%s.\n",
+ Alert("parsing [%s:%d]: 'http-response' expects 'allow', 'deny', 'redirect', 'add-header', 'del-header', 'set-header', 'set-nice', 'set-tos', 'set-mark', 'set-log-level', 'del-acl', 'add-acl', 'del-map', 'set-map', but got '%s'%s.\n",
file, linenum, args[0], *args[0] ? "" : " (missing argument)");
goto out_err;
}