MEDIUM: log-format: make the LF parser aware of sample expressions' end
For a very long time it used to be impossible to pass a closing square
bracket as a valid character in argument to a sample fetch function or
to a converter because the LF parser used to stop on the first such
character found and to pass what was between the first '[' and the first
']' to sample_parse_expr().
This patch addresses this by passing the whole string to sample_parse_expr()
which is the only one authoritative to indicate the first character that
does not belong to the expression. The LF parser then verifies it matches
a ']' or fails. As a result it is finally possible to write rules such as
the following, which is totally valid an unambigous :
http-request redirect location %[url,regsub([.:/?-],!,g)]
|-----| | |
arg1 | `---> arg3
`-----> arg2
|-----------------|
converter
|---------------------|
sample expression
|------------------------|
log-format tag
diff --git a/doc/configuration.txt b/doc/configuration.txt
index 8bf0506..392742e 100644
--- a/doc/configuration.txt
+++ b/doc/configuration.txt
@@ -14151,13 +14151,16 @@
second level is usable for argument. It is recommended to use single quotes
outside since these ones do not try to resolve backslashes nor dollar signs.
- Example :
+ Examples:
# de-duplicate "/" in header "x-path".
# input: x-path: /////a///b/c/xzxyz/
# output: x-path: /a/b/c/xzxyz/
http-request set-header x-path "%[hdr(x-path),regsub('/+','/','g')]"
+ # copy query string to x-query and drop all leading '?', ';' and '&'
+ http-request set-header x-query "%[query,regsub([?;&]*,'')]"
+
capture-req(<id>)
Capture the string entry in the request slot <id> and returns the entry as
is. If the slot doesn't exist, the capture fails silently.
diff --git a/src/log.c b/src/log.c
index 51d684d..40eac40 100644
--- a/src/log.c
+++ b/src/log.c
@@ -473,11 +473,13 @@
/*
* Parse the sample fetch expression <text> and add a node to <list_format> upon
* success. At the moment, sample converters are not yet supported but fetch arguments
- * should work. The curpx->conf.args.ctx must be set by the caller.
+ * should work. The curpx->conf.args.ctx must be set by the caller. If an end pointer
+ * is passed in <endptr>, it will be updated with the pointer to the first character
+ * not part of the sample expression.
*
* In error case, the function returns 0, otherwise it returns 1.
*/
-int add_sample_to_logformat_list(char *text, char *arg, int arg_len, struct proxy *curpx, struct list *list_format, int options, int cap, char **err)
+int add_sample_to_logformat_list(char *text, char *arg, int arg_len, struct proxy *curpx, struct list *list_format, int options, int cap, char **err, char **endptr)
{
char *cmd[2];
struct sample_expr *expr = NULL;
@@ -488,7 +490,7 @@
cmd[1] = "";
cmd_arg = 0;
- expr = sample_parse_expr(cmd, &cmd_arg, curpx->conf.args.file, curpx->conf.args.line, err, &curpx->conf.args, NULL);
+ expr = sample_parse_expr(cmd, &cmd_arg, curpx->conf.args.file, curpx->conf.args.line, err, &curpx->conf.args, endptr);
if (!expr) {
memprintf(err, "failed to parse sample expression <%s> : %s", text, *err);
goto error_free;
@@ -648,10 +650,26 @@
goto fail;
case LF_STEXPR: // text immediately following '%['
- if (*str == ']') { // end of arg
- cformat = LF_EDEXPR;
- var_len = str - var;
- *str = 0; // needed for parsing the expression
+ /* the whole sample expression is parsed at once,
+ * returning the pointer to the first character not
+ * part of the expression, which MUST be the trailing
+ * angle bracket.
+ */
+ if (!add_sample_to_logformat_list(var, arg, arg_len, curproxy, list_format, options, cap, err, &str))
+ goto fail;
+
+ if (*str == ']') {
+ // end of arg, go on with next state
+ cformat = pformat = LF_EDEXPR;
+ sp = str;
+ }
+ else {
+ char c = *str;
+ *str = 0;
+ if (isprint(c))
+ memprintf(err, "expected ']' after '%s', but found '%c'", var, c);
+ else
+ memprintf(err, "missing ']' after '%s'", var);
}
break;
@@ -681,7 +699,7 @@
goto fail;
break;
case LF_STEXPR:
- if (!add_sample_to_logformat_list(var, arg, arg_len, curproxy, list_format, options, cap, err))
+ if (!add_sample_to_logformat_list(var, arg, arg_len, curproxy, list_format, options, cap, err, &sp))
goto fail;
break;
case LF_TEXT: