MEDIUM: http: merge acl and pattern header fetch functions
HTTP header fetch is now done using smp_fetch_hdr() for both ACLs and
patterns. This one also supports an occurrence number, making it possible
to specify explicit occurrences for ACLs and patterns.
diff --git a/doc/configuration.txt b/doc/configuration.txt
index 20bb050..979af63 100644
--- a/doc/configuration.txt
+++ b/doc/configuration.txt
@@ -7927,12 +7927,16 @@
scook_sub() variant for response cookies sent by the server.
hdr <string>
-hdr(<header>) <string>
+hdr(<header>[,<occ>]) <string>
Note: all the "hdr*" matching criteria either apply to all headers, or to a
particular header whose name is passed between parenthesis and without any
space. The header name is not case-sensitive. The header matching complies
with RFC2616, and treats as separate headers all values delimited by commas.
- Use the shdr() variant for response headers sent by the server.
+ If an occurrence number is specified as the optional second argument, only
+ this occurrence will be considered. Positive values indicate a position from
+ the first occurrence, 1 being the first one. Negative values indicate
+ positions relative to the last one, -1 being the last one. Use the shdr()
+ variant for response headers sent by the server.
The "hdr" criteria returns true if any of the headers matching the criteria
match any of the strings. This can be used to check for exact values. For
@@ -7941,7 +7945,7 @@
hdr(Connection) -i close
hdr_beg <string>
-hdr_beg(<header>) <string>
+hdr_beg(<header>[,<occ>]) <string>
Returns true when one of the headers begins with one of the strings. See
"hdr" for more information on header matching. Use the shdr_beg() variant for
response headers sent by the server.
@@ -7957,7 +7961,7 @@
the shdr_cnt() variant for response headers sent by the server.
hdr_dir <string>
-hdr_dir(<header>) <string>
+hdr_dir(<header>[,<occ>]) <string>
Returns true when one of the headers contains one of the strings either
isolated or delimited by slashes. This is used to perform filename or
directory name matching, and may be used with Referer. See "hdr" for more
@@ -7965,7 +7969,7 @@
headers sent by the server.
hdr_dom <string>
-hdr_dom(<header>) <string>
+hdr_dom(<header>[,<occ>]) <string>
Returns true when one of the headers contains one of the strings either
isolated or delimited by dots. This is used to perform domain name matching,
and may be used with the Host header. See "hdr" for more information on
@@ -7973,27 +7977,27 @@
server.
hdr_end <string>
-hdr_end(<header>) <string>
+hdr_end(<header>[,<occ>]) <string>
Returns true when one of the headers ends with one of the strings. See "hdr"
for more information on header matching. Use the shdr_end() variant for
response headers sent by the server.
hdr_ip <ip_address>
-hdr_ip(<header>) <ip_address>
+hdr_ip(<header>[,<occ>]) <ip_address>
Returns true when one of the headers' values contains an IP address matching
<ip_address>. This is mainly used with headers such as X-Forwarded-For or
X-Client-IP. See "hdr" for more information on header matching. Use the
shdr_ip() variant for response headers sent by the server.
hdr_len <integer>
-hdr_len(<header>) <integer>
+hdr_len(<header>[,<occ>]) <integer>
Returns true when at least one of the headers has a length which matches the
values or ranges specified. This may be used to detect empty or too large
headers. See "hdr" for more information on header matching. Use the
shdr_len() variant for response headers sent by the server.
hdr_reg <regex>
-hdr_reg(<header>) <regex>
+hdr_reg(<header>[,<occ>]) <regex>
Returns true it one of the headers matches one of the regular expressions. It
can be used at any time, but it is important to remember that regex matching
is slower than other methods. See also other "hdr_" criteria, as well as
@@ -8001,13 +8005,13 @@
response headers sent by the server.
hdr_sub <string>
-hdr_sub(<header>) <string>
+hdr_sub(<header>[,<occ>]) <string>
Returns true when one of the headers contains one of the strings. See "hdr"
for more information on header matching. Use the shdr_sub() variant for
response headers sent by the server.
hdr_val <integer>
-hdr_val(<header>) <integer>
+hdr_val(<header>[,<occ>]) <integer>
Returns true when one of the headers starts with a number which matches the
values or ranges specified. This may be used to limit content-length to
acceptable values for example. See "hdr" for more information on header
@@ -8331,8 +8335,13 @@
ports to some clients for a whole application session. It is of
type integer and only works with such tables.
- hdr(<name>) This extracts the last occurrence of header <name> in an HTTP
- request. A typical use is with the X-Forwarded-For header once
+ hdr(<name>[,<occ>])
+ This extracts the last occurrence of header <name> in an HTTP
+ request. Optionally, a specific occurrence might be specified as
+ a position number. Positive values indicate a position from the
+ first occurrence, with 1 being the first one. Negative values
+ indicate positions relative to the last one, with -1 being the
+ last one. A typical use is with the X-Forwarded-For header once
converted to IP, associated with an IP stick-table.
payload(<offset>,<length>)
diff --git a/include/proto/proto_http.h b/include/proto/proto_http.h
index 16f03c2..06fe292 100644
--- a/include/proto/proto_http.h
+++ b/include/proto/proto_http.h
@@ -98,7 +98,7 @@
void http_capture_bad_message(struct error_snapshot *es, struct session *s,
struct http_msg *msg,
int state, struct proxy *other_end);
-unsigned int http_get_hdr(struct http_msg *msg, const char *hname, int hlen,
+unsigned int http_get_hdr(const struct http_msg *msg, const char *hname, int hlen,
struct hdr_idx *idx, int occ,
struct hdr_ctx *ctx, char **vptr, int *vlen);
diff --git a/src/proto_http.c b/src/proto_http.c
index 08750d7..a1b7e13 100644
--- a/src/proto_http.c
+++ b/src/proto_http.c
@@ -7261,7 +7261,7 @@
* -1.
* The return value is 0 if nothing was found, or non-zero otherwise.
*/
-unsigned int http_get_hdr(struct http_msg *msg, const char *hname, int hlen,
+unsigned int http_get_hdr(const struct http_msg *msg, const char *hname, int hlen,
struct hdr_idx *idx, int occ,
struct hdr_ctx *ctx, char **vptr, int *vlen)
{
@@ -7843,39 +7843,54 @@
return 1;
}
-/* 5. Check on HTTP header. A pointer to the beginning of the value is returned.
- * Accepts exactly 1 argument of type string.
+/* Fetch an HTTP header. A pointer to the beginning of the value is returned.
+ * Accepts an optional argument of type string containing the header field name,
+ * and an optional argument of type signed or unsigned integer to request an
+ * explicit occurrence of the header. Note that in the event of a missing name,
+ * headers are considered from the first one.
*/
static int
-acl_fetch_hdr(struct proxy *px, struct session *l4, void *l7, unsigned int opt,
+smp_fetch_hdr(struct proxy *px, struct session *l4, void *l7, unsigned int opt,
const struct arg *args, struct sample *smp)
{
struct http_txn *txn = l7;
struct hdr_idx *idx = &txn->hdr_idx;
struct hdr_ctx *ctx = (struct hdr_ctx *)smp->ctx.a;
const struct http_msg *msg = ((opt & SMP_OPT_DIR) == SMP_OPT_DIR_REQ) ? &txn->req : &txn->rsp;
+ int occ = 0;
+ const char *name_str = NULL;
+ int name_len = 0;
- if (!args || args->type != ARGT_STR)
- return 0;
+ if (args) {
+ if (args[0].type != ARGT_STR)
+ return 0;
+ name_str = args[0].data.str.str;
+ name_len = args[0].data.str.len;
+
+ if (args[1].type == ARGT_UINT || args[1].type == ARGT_SINT)
+ occ = args[1].data.uint;
+ }
CHECK_HTTP_MESSAGE_FIRST();
- if (!(smp->flags & SMP_F_NOT_LAST))
+ if (ctx && !(smp->flags & SMP_F_NOT_LAST))
/* search for header from the beginning */
ctx->idx = 0;
+ if (!occ && !(opt & SMP_OPT_ITERATE))
+ /* no explicit occurrence and single fetch => last header by default */
+ occ = -1;
+
- if (http_find_header2(args->data.str.str, args->data.str.len, msg->buf->p + msg->sol, idx, ctx)) {
+ if (!occ)
+ /* prepare to report multiple occurrences for ACL fetches */
smp->flags |= SMP_F_NOT_LAST;
- smp->flags |= SMP_F_VOL_HDR;
- smp->type = SMP_T_CSTR;
- smp->data.str.str = (char *)ctx->line + ctx->val;
- smp->data.str.len = ctx->vlen;
+ smp->type = SMP_T_CSTR;
+ smp->flags |= SMP_F_VOL_HDR;
+ if (http_get_hdr(msg, name_str, name_len, idx, occ, ctx, &smp->data.str.str, &smp->data.str.len))
return 1;
- }
smp->flags &= ~SMP_F_NOT_LAST;
- smp->flags |= SMP_F_VOL_HDR;
return 0;
}
@@ -7883,7 +7898,7 @@
* Accepts exactly 1 argument of type string.
*/
static int
-acl_fetch_hdr_cnt(struct proxy *px, struct session *l4, void *l7, unsigned int opt,
+smp_fetch_hdr_cnt(struct proxy *px, struct session *l4, void *l7, unsigned int opt,
const struct arg *args, struct sample *smp)
{
struct http_txn *txn = l7;
@@ -7908,14 +7923,16 @@
return 1;
}
-/* 7. Check on HTTP header's integer value. The integer value is returned.
- * FIXME: the type is 'int', it may not be appropriate for everything.
+/* Fetch an HTTP header's integer value. The integer value is returned. It
+ * takes a mandatory argument of type string and an optional one of type int
+ * to designate a specific occurrence. It returns an unsigned integer, which
+ * may or may not be appropriate for everything.
*/
static int
-acl_fetch_hdr_val(struct proxy *px, struct session *l4, void *l7, unsigned int opt,
+smp_fetch_hdr_val(struct proxy *px, struct session *l4, void *l7, unsigned int opt,
const struct arg *args, struct sample *smp)
{
- int ret = acl_fetch_hdr(px, l4, l7, opt, args, smp);
+ int ret = smp_fetch_hdr(px, l4, l7, opt, args, smp);
if (ret > 0) {
smp->type = SMP_T_UINT;
@@ -7925,19 +7942,23 @@
return ret;
}
-/* 7. Check on HTTP header's IPv4 address value. The IPv4 address is returned.
+/* Fetch an HTTP header's integer value. The integer value is returned. It
+ * takes a mandatory argument of type string and an optional one of type int
+ * to designate a specific occurrence. It returns an IPv4 address.
*/
static int
-acl_fetch_hdr_ip(struct proxy *px, struct session *l4, void *l7, unsigned int opt,
+smp_fetch_hdr_ip(struct proxy *px, struct session *l4, void *l7, unsigned int opt,
const struct arg *args, struct sample *smp)
{
int ret;
- while ((ret = acl_fetch_hdr(px, l4, l7, opt, args, smp)) > 0) {
+ while ((ret = smp_fetch_hdr(px, l4, l7, opt, args, smp)) > 0) {
smp->type = SMP_T_IPV4;
if (url2ipv4((char *)smp->data.str.str, &smp->data.ipv4))
break;
/* if the header doesn't match an IP address, fetch next one */
+ if (!(smp->flags & SMP_F_NOT_LAST))
+ return 0;
}
return ret;
}
@@ -8260,18 +8281,6 @@
/* The code below is dedicated to pattern fetching and matching */
/************************************************************************/
-/* Returns the last occurrence of specified header. */
-static int
-pattern_fetch_hdr(struct proxy *px, struct session *l4, void *l7, unsigned int opt,
- const struct arg *arg_p, struct sample *smp)
-{
- struct http_txn *txn = l7;
-
- smp->type = SMP_T_CSTR;
- return http_get_hdr(&txn->req, arg_p->data.str.str, arg_p->data.str.len, &txn->hdr_idx,
- -1, NULL, &smp->data.str.str, &smp->data.str.len);
-}
-
/*
* Given a path string and its length, find the position of beginning of the
* query string. Returns NULL if no query string is found in the path.
@@ -8458,6 +8467,24 @@
return found;
}
+/* This function is used to validate the arguments passed to any "hdr" fetch
+ * keyword. These keywords support an optional positive or negative occurrence
+ * number. We must ensure that the number is greater than -MAX_HDR_HISTORY. It
+ * is assumed that the types are already the correct ones. Returns 0 on error,
+ * non-zero if OK. If <err> is not NULL, it will be filled with a pointer to an
+ * error message in case of error, that the caller is responsible for freeing.
+ * The initial location must either be freeable or NULL.
+ */
+static int val_hdr(struct arg *arg, char **err_msg)
+{
+ if (arg && arg[1].type == ARGT_SINT && arg[1].data.sint < -MAX_HDR_HISTORY) {
+ if (err_msg)
+ memprintf(err_msg, "header occurrence must be >= %d", -MAX_HDR_HISTORY);
+ return 0;
+ }
+ return 1;
+}
+
/************************************************************************/
/* All supported ACL keywords must be declared here. */
/************************************************************************/
@@ -8476,17 +8503,17 @@
{ "cook_reg", acl_parse_reg, acl_fetch_cookie_value, acl_match_reg, ACL_USE_L7REQ_VOLATILE, ARG1(0,STR) },
{ "cook_sub", acl_parse_str, acl_fetch_cookie_value, acl_match_sub, ACL_USE_L7REQ_VOLATILE, ARG1(0,STR) },
- { "hdr", acl_parse_str, acl_fetch_hdr, acl_match_str, ACL_USE_L7REQ_VOLATILE|ACL_MAY_LOOKUP, ARG1(0,STR) },
- { "hdr_beg", acl_parse_str, acl_fetch_hdr, acl_match_beg, ACL_USE_L7REQ_VOLATILE, ARG1(0,STR) },
- { "hdr_cnt", acl_parse_int, acl_fetch_hdr_cnt, acl_match_int, ACL_USE_L7REQ_VOLATILE, ARG1(0,STR) },
- { "hdr_dir", acl_parse_str, acl_fetch_hdr, acl_match_dir, ACL_USE_L7REQ_VOLATILE, ARG1(0,STR) },
- { "hdr_dom", acl_parse_str, acl_fetch_hdr, acl_match_dom, ACL_USE_L7REQ_VOLATILE, ARG1(0,STR) },
- { "hdr_end", acl_parse_str, acl_fetch_hdr, acl_match_end, ACL_USE_L7REQ_VOLATILE, ARG1(0,STR) },
- { "hdr_ip", acl_parse_ip, acl_fetch_hdr_ip, acl_match_ip, ACL_USE_L7REQ_VOLATILE|ACL_MAY_LOOKUP, ARG1(0,STR) },
- { "hdr_len", acl_parse_int, acl_fetch_hdr, acl_match_len, ACL_USE_L7REQ_VOLATILE, ARG1(0,STR) },
- { "hdr_reg", acl_parse_reg, acl_fetch_hdr, acl_match_reg, ACL_USE_L7REQ_VOLATILE, ARG1(0,STR) },
- { "hdr_sub", acl_parse_str, acl_fetch_hdr, acl_match_sub, ACL_USE_L7REQ_VOLATILE, ARG1(0,STR) },
- { "hdr_val", acl_parse_int, acl_fetch_hdr_val, acl_match_int, ACL_USE_L7REQ_VOLATILE, ARG1(0,STR) },
+ { "hdr", acl_parse_str, smp_fetch_hdr, acl_match_str, ACL_USE_L7REQ_VOLATILE|ACL_MAY_LOOKUP, ARG2(0,STR,SINT), val_hdr },
+ { "hdr_beg", acl_parse_str, smp_fetch_hdr, acl_match_beg, ACL_USE_L7REQ_VOLATILE, ARG2(0,STR,SINT), val_hdr },
+ { "hdr_cnt", acl_parse_int, smp_fetch_hdr_cnt, acl_match_int, ACL_USE_L7REQ_VOLATILE, ARG1(0,STR) },
+ { "hdr_dir", acl_parse_str, smp_fetch_hdr, acl_match_dir, ACL_USE_L7REQ_VOLATILE, ARG2(0,STR,SINT), val_hdr },
+ { "hdr_dom", acl_parse_str, smp_fetch_hdr, acl_match_dom, ACL_USE_L7REQ_VOLATILE, ARG2(0,STR,SINT), val_hdr },
+ { "hdr_end", acl_parse_str, smp_fetch_hdr, acl_match_end, ACL_USE_L7REQ_VOLATILE, ARG2(0,STR,SINT), val_hdr },
+ { "hdr_ip", acl_parse_ip, smp_fetch_hdr_ip, acl_match_ip, ACL_USE_L7REQ_VOLATILE|ACL_MAY_LOOKUP, ARG2(0,STR,SINT), val_hdr },
+ { "hdr_len", acl_parse_int, smp_fetch_hdr, acl_match_len, ACL_USE_L7REQ_VOLATILE, ARG2(0,STR,SINT), val_hdr },
+ { "hdr_reg", acl_parse_reg, smp_fetch_hdr, acl_match_reg, ACL_USE_L7REQ_VOLATILE, ARG2(0,STR,SINT), val_hdr },
+ { "hdr_sub", acl_parse_str, smp_fetch_hdr, acl_match_sub, ACL_USE_L7REQ_VOLATILE, ARG2(0,STR,SINT), val_hdr },
+ { "hdr_val", acl_parse_int, smp_fetch_hdr_val, acl_match_int, ACL_USE_L7REQ_VOLATILE, ARG2(0,STR,SINT), val_hdr },
{ "http_auth", acl_parse_nothing, acl_fetch_http_auth, acl_match_nothing, ACL_USE_L7REQ_VOLATILE, ARG1(0,USR) },
{ "http_auth_group", acl_parse_strcat, acl_fetch_http_auth, acl_match_auth, ACL_USE_L7REQ_VOLATILE, ARG1(0,USR) },
@@ -8517,17 +8544,17 @@
{ "scook_reg", acl_parse_reg, acl_fetch_cookie_value, acl_match_reg, ACL_USE_L7RTR_VOLATILE, ARG1(0,STR) },
{ "scook_sub", acl_parse_str, acl_fetch_cookie_value, acl_match_sub, ACL_USE_L7RTR_VOLATILE, ARG1(0,STR) },
- { "shdr", acl_parse_str, acl_fetch_hdr, acl_match_str, ACL_USE_L7RTR_VOLATILE|ACL_MAY_LOOKUP, ARG1(0,STR) },
- { "shdr_beg", acl_parse_str, acl_fetch_hdr, acl_match_beg, ACL_USE_L7RTR_VOLATILE, ARG1(0,STR) },
- { "shdr_cnt", acl_parse_int, acl_fetch_hdr_cnt, acl_match_int, ACL_USE_L7RTR_VOLATILE, ARG1(0,STR) },
- { "shdr_dir", acl_parse_str, acl_fetch_hdr, acl_match_dir, ACL_USE_L7RTR_VOLATILE, ARG1(0,STR) },
- { "shdr_dom", acl_parse_str, acl_fetch_hdr, acl_match_dom, ACL_USE_L7RTR_VOLATILE, ARG1(0,STR) },
- { "shdr_end", acl_parse_str, acl_fetch_hdr, acl_match_end, ACL_USE_L7RTR_VOLATILE, ARG1(0,STR) },
- { "shdr_ip", acl_parse_ip, acl_fetch_hdr_ip, acl_match_ip, ACL_USE_L7RTR_VOLATILE|ACL_MAY_LOOKUP, ARG1(0,STR) },
- { "shdr_len", acl_parse_int, acl_fetch_hdr, acl_match_len, ACL_USE_L7RTR_VOLATILE, ARG1(0,STR) },
- { "shdr_reg", acl_parse_reg, acl_fetch_hdr, acl_match_reg, ACL_USE_L7RTR_VOLATILE, ARG1(0,STR) },
- { "shdr_sub", acl_parse_str, acl_fetch_hdr, acl_match_sub, ACL_USE_L7RTR_VOLATILE, ARG1(0,STR) },
- { "shdr_val", acl_parse_int, acl_fetch_hdr_val, acl_match_int, ACL_USE_L7RTR_VOLATILE, ARG1(0,STR) },
+ { "shdr", acl_parse_str, smp_fetch_hdr, acl_match_str, ACL_USE_L7RTR_VOLATILE|ACL_MAY_LOOKUP, ARG2(0,STR,SINT), val_hdr },
+ { "shdr_beg", acl_parse_str, smp_fetch_hdr, acl_match_beg, ACL_USE_L7RTR_VOLATILE, ARG2(0,STR,SINT), val_hdr },
+ { "shdr_cnt", acl_parse_int, smp_fetch_hdr_cnt, acl_match_int, ACL_USE_L7RTR_VOLATILE, ARG1(0,STR) },
+ { "shdr_dir", acl_parse_str, smp_fetch_hdr, acl_match_dir, ACL_USE_L7RTR_VOLATILE, ARG2(0,STR,SINT), val_hdr },
+ { "shdr_dom", acl_parse_str, smp_fetch_hdr, acl_match_dom, ACL_USE_L7RTR_VOLATILE, ARG2(0,STR,SINT), val_hdr },
+ { "shdr_end", acl_parse_str, smp_fetch_hdr, acl_match_end, ACL_USE_L7RTR_VOLATILE, ARG2(0,STR,SINT), val_hdr },
+ { "shdr_ip", acl_parse_ip, smp_fetch_hdr_ip, acl_match_ip, ACL_USE_L7RTR_VOLATILE|ACL_MAY_LOOKUP, ARG2(0,STR,SINT), val_hdr },
+ { "shdr_len", acl_parse_int, smp_fetch_hdr, acl_match_len, ACL_USE_L7RTR_VOLATILE, ARG2(0,STR,SINT), val_hdr },
+ { "shdr_reg", acl_parse_reg, smp_fetch_hdr, acl_match_reg, ACL_USE_L7RTR_VOLATILE, ARG2(0,STR,SINT), val_hdr },
+ { "shdr_sub", acl_parse_str, smp_fetch_hdr, acl_match_sub, ACL_USE_L7RTR_VOLATILE, ARG2(0,STR,SINT), val_hdr },
+ { "shdr_val", acl_parse_int, smp_fetch_hdr_val, acl_match_int, ACL_USE_L7RTR_VOLATILE, ARG2(0,STR,SINT), val_hdr },
{ "status", acl_parse_int, acl_fetch_stcode, acl_match_int, ACL_USE_L7RTR_PERMANENT, 0 },
@@ -8560,7 +8587,7 @@
/************************************************************************/
/* Note: must not be declared <const> as its list will be overwritten */
static struct pattern_fetch_kw_list pattern_fetch_keywords = {{ },{
- { "hdr", pattern_fetch_hdr, ARG1(1,STR), NULL, SMP_T_CSTR, SMP_CAP_REQ },
+ { "hdr", smp_fetch_hdr, ARG2(1,STR,SINT), val_hdr, SMP_T_CSTR, SMP_CAP_REQ },
{ "url_param", smp_fetch_url_param, ARG1(1,STR), NULL, SMP_T_CSTR, SMP_CAP_REQ },
{ "cookie", pattern_fetch_cookie, ARG1(1,STR), NULL, SMP_T_CSTR, SMP_CAP_REQ },
{ "set-cookie", pattern_fetch_set_cookie, ARG1(1,STR), NULL, SMP_T_CSTR, SMP_CAP_RES },