From cd0d2ed6ee932e0d86b8fbaf5fcb97d9530bf260 Mon Sep 17 00:00:00 2001 From: Willy Tarreau Date: Fri, 14 Feb 2020 17:33:06 +0100 Subject: [PATCH] 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 --- doc/configuration.txt | 5 ++++- src/log.c | 34 ++++++++++++++++++++++++++-------- 2 files changed, 30 insertions(+), 9 deletions(-) diff --git a/doc/configuration.txt b/doc/configuration.txt index 8bf0506d5..392742eea 100644 --- a/doc/configuration.txt +++ b/doc/configuration.txt @@ -14151,13 +14151,16 @@ regsub(,[,]) 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() Capture the string entry in the request slot 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 51d684d6e..40eac40a9 100644 --- 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 and add a node to 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 , 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: