]> git.ipfire.org Git - thirdparty/haproxy.git/commitdiff
MEDIUM: log-format: make the LF parser aware of sample expressions' end
authorWilly Tarreau <w@1wt.eu>
Fri, 14 Feb 2020 16:33:06 +0000 (17:33 +0100)
committerWilly Tarreau <w@1wt.eu>
Fri, 14 Feb 2020 18:02:06 +0000 (19:02 +0100)
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

doc/configuration.txt
src/log.c

index 8bf0506d55a2ab497c8ff60e527df753a00c3497..392742eea3a65dda21d68441bc4457da9dc03b95 100644 (file)
@@ -14151,13 +14151,16 @@ regsub(<regex>,<subst>[,<flags>])
   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.
index 51d684d6e746a0db828e150515ce7699ddc07ca2..40eac40a99b8d2a564da146d65ee0b222144e729 100644 (file)
--- a/src/log.c
+++ b/src/log.c
@@ -473,11 +473,13 @@ int add_to_logformat_list(char *start, char *end, int type, struct list *list_fo
 /*
  * 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 @@ int add_sample_to_logformat_list(char *text, char *arg, int arg_len, struct prox
        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 @@ int parse_logformat_string(const char *fmt, struct proxy *curproxy, struct list
                        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 @@ int parse_logformat_string(const char *fmt, struct proxy *curproxy, struct list
                                        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: