mirror of
http://git.haproxy.org/git/haproxy.git/
synced 2025-03-03 18:09:25 +00:00
MEDIUM: sample: handle comma-delimited converter list
We now support having a comma-delimited converter list, which can start right after the fetch keyword. The immediate benefit is that it allows to use converters in log-format expressions, for example : set-header source-net %[src,ipmask(24)] The parser is also slightly improved and should be more resilient against configuration errors. Also, optional arguments in converters were mistakenly not allowed till now, so this was fixed.
This commit is contained in:
parent
fa8e2bc68c
commit
833cc79434
@ -8699,8 +8699,10 @@ sample fetch system. The "stick on", and "stick store-request" directives
|
||||
support sample fetch rules which allow a list of transformations to be applied
|
||||
on top of the fetched sample, and the finaly result is automatically converted
|
||||
to the type of the table. These transformations are enumerated as a series
|
||||
of specific keywords after the sample fetch method. These keywords can also
|
||||
support some arguments (eg: a netmask) which must be passed in parenthesis.
|
||||
of specific keywords after the sample fetch method. These keywords may equally
|
||||
be appended immediately after the fetch keyword's argument, delimited by a
|
||||
comma. These keywords can also support some arguments (eg: a netmask) which
|
||||
must be passed in parenthesis.
|
||||
|
||||
The currently available list of transformation keywords include :
|
||||
|
||||
|
214
src/sample.c
214
src/sample.c
@ -534,55 +534,59 @@ static sample_cast_fct sample_casts[SMP_TYPES][SMP_TYPES] = {
|
||||
*/
|
||||
struct sample_expr *sample_parse_expr(char **str, int *idx, char *err, int err_size, struct arg_list *al)
|
||||
{
|
||||
const char *endw;
|
||||
const char *end;
|
||||
const char *begw; /* beginning of word */
|
||||
const char *endw; /* end of word */
|
||||
const char *endt; /* end of term */
|
||||
struct sample_expr *expr;
|
||||
struct sample_fetch *fetch;
|
||||
struct sample_conv *conv;
|
||||
unsigned long prev_type;
|
||||
char *p;
|
||||
char *fkw = NULL;
|
||||
char *ckw = NULL;
|
||||
|
||||
/* prepare a generic message if any further snprintf() fails */
|
||||
snprintf(err, err_size, "memory error.");
|
||||
if (!str[*idx]) {
|
||||
|
||||
begw = str[*idx];
|
||||
for (endw = begw; *endw && *endw != '(' && *endw != ','; endw++);
|
||||
|
||||
if (endw == begw) {
|
||||
snprintf(err, err_size, "missing fetch method.");
|
||||
goto out_error;
|
||||
}
|
||||
|
||||
end = str[*idx] + strlen(str[*idx]);
|
||||
endw = strchr(str[*idx], '(');
|
||||
/* keep a copy of the current fetch keyword for error reporting */
|
||||
fkw = my_strndup(begw, endw - begw);
|
||||
|
||||
if (!endw)
|
||||
endw = end;
|
||||
else if ((end-1)[0] != ')') {
|
||||
p = my_strndup(str[*idx], endw - str[*idx]);
|
||||
if (p) {
|
||||
snprintf(err, err_size, "syntax error: missing ')' after keyword '%s'.", p);
|
||||
free(p);
|
||||
}
|
||||
goto out_error;
|
||||
}
|
||||
|
||||
fetch = find_sample_fetch(str[*idx], endw - str[*idx]);
|
||||
fetch = find_sample_fetch(begw, endw - begw);
|
||||
if (!fetch) {
|
||||
p = my_strndup(str[*idx], endw - str[*idx]);
|
||||
if (p) {
|
||||
snprintf(err, err_size, "unknown fetch method '%s'.", p);
|
||||
free(p);
|
||||
}
|
||||
snprintf(err, err_size, "unknown fetch method '%s'.", fkw);
|
||||
goto out_error;
|
||||
}
|
||||
|
||||
endt = endw;
|
||||
if (*endt == '(') {
|
||||
/* look for the end of this term */
|
||||
while (*endt && *endt != ')')
|
||||
endt++;
|
||||
if (*endt != ')') {
|
||||
snprintf(err, err_size, "syntax error: missing ')' after fetch keyword '%s'.", fkw);
|
||||
goto out_error;
|
||||
}
|
||||
}
|
||||
|
||||
/* At this point, we have :
|
||||
* - begw : beginning of the keyword
|
||||
* - endw : end of the keyword (points to next delimiter or '(')
|
||||
* - endt : end of the term (=endw or last parenthesis if args are present)
|
||||
*/
|
||||
|
||||
if (fetch->out_type >= SMP_TYPES) {
|
||||
|
||||
p = my_strndup(str[*idx], endw - str[*idx]);
|
||||
if (p) {
|
||||
snprintf(err, err_size, "returns type of fetch method '%s' is unknown.", p);
|
||||
free(p);
|
||||
}
|
||||
snprintf(err, err_size, "returns type of fetch method '%s' is unknown.", fkw);
|
||||
goto out_error;
|
||||
}
|
||||
|
||||
prev_type = fetch->out_type;
|
||||
|
||||
expr = calloc(1, sizeof(struct sample_expr));
|
||||
if (!expr)
|
||||
goto out_error;
|
||||
@ -591,27 +595,19 @@ struct sample_expr *sample_parse_expr(char **str, int *idx, char *err, int err_s
|
||||
expr->fetch = fetch;
|
||||
expr->arg_p = empty_arg_list;
|
||||
|
||||
if (end != endw) {
|
||||
if (endt != endw) {
|
||||
char *err_msg = NULL;
|
||||
int err_arg;
|
||||
|
||||
if (!fetch->arg_mask) {
|
||||
p = my_strndup(str[*idx], endw - str[*idx]);
|
||||
if (p) {
|
||||
snprintf(err, err_size, "fetch method '%s' does not support any args.", p);
|
||||
free(p);
|
||||
}
|
||||
snprintf(err, err_size, "fetch method '%s' does not support any args.", fkw);
|
||||
goto out_error;
|
||||
}
|
||||
|
||||
al->kw = expr->fetch->kw;
|
||||
al->conv = NULL;
|
||||
if (make_arg_list(endw + 1, end - endw - 2, fetch->arg_mask, &expr->arg_p, &err_msg, NULL, &err_arg, al) < 0) {
|
||||
p = my_strndup(str[*idx], endw - str[*idx]);
|
||||
if (p) {
|
||||
snprintf(err, err_size, "invalid arg %d in fetch method '%s' : %s.", err_arg+1, p, err_msg);
|
||||
free(p);
|
||||
}
|
||||
if (make_arg_list(endw + 1, endt - endw - 1, fetch->arg_mask, &expr->arg_p, &err_msg, NULL, &err_arg, al) < 0) {
|
||||
snprintf(err, err_size, "invalid arg %d in fetch method '%s' : %s.", err_arg+1, fkw, err_msg);
|
||||
free(err_msg);
|
||||
goto out_error;
|
||||
}
|
||||
@ -620,62 +616,88 @@ struct sample_expr *sample_parse_expr(char **str, int *idx, char *err, int err_s
|
||||
expr->arg_p = empty_arg_list;
|
||||
|
||||
if (fetch->val_args && !fetch->val_args(expr->arg_p, &err_msg)) {
|
||||
p = my_strndup(str[*idx], endw - str[*idx]);
|
||||
if (p) {
|
||||
snprintf(err, err_size, "invalid args in fetch method '%s' : %s.", p, err_msg);
|
||||
free(p);
|
||||
}
|
||||
snprintf(err, err_size, "invalid args in fetch method '%s' : %s.", fkw, err_msg);
|
||||
free(err_msg);
|
||||
goto out_error;
|
||||
}
|
||||
}
|
||||
else if (ARGM(fetch->arg_mask)) {
|
||||
p = my_strndup(str[*idx], endw - str[*idx]);
|
||||
if (p) {
|
||||
snprintf(err, err_size, "missing args for fetch method '%s'.", p);
|
||||
free(p);
|
||||
}
|
||||
snprintf(err, err_size, "missing args for fetch method '%s'.", fkw);
|
||||
goto out_error;
|
||||
}
|
||||
|
||||
for (*idx += 1; *(str[*idx]); (*idx)++) {
|
||||
/* Now process the converters if any. We have two supported syntaxes
|
||||
* for the converters, which can be combined :
|
||||
* - comma-delimited list of converters just after the keyword and args ;
|
||||
* - one converter per keyword
|
||||
* The combination allows to have each keyword being a comma-delimited
|
||||
* series of converters.
|
||||
*
|
||||
* We want to process the former first, then the latter. For this we start
|
||||
* from the beginning of the supposed place in the exiting conv chain, which
|
||||
* starts at the last comma (endt).
|
||||
*/
|
||||
|
||||
while (1) {
|
||||
struct sample_conv_expr *conv_expr;
|
||||
|
||||
end = str[*idx] + strlen(str[*idx]);
|
||||
endw = strchr(str[*idx], '(');
|
||||
if (*endt == ')') /* skip last closing parenthesis */
|
||||
endt++;
|
||||
|
||||
if (!endw)
|
||||
endw = end;
|
||||
else if ((end-1)[0] != ')') {
|
||||
p = my_strndup(str[*idx], endw - str[*idx]);
|
||||
if (p) {
|
||||
snprintf(err, err_size, "syntax error, missing ')' after keyword '%s'.", p);
|
||||
free(p);
|
||||
}
|
||||
if (*endt && *endt != ',') {
|
||||
if (ckw)
|
||||
snprintf(err, err_size, "missing comma after conv keyword '%s'.", ckw);
|
||||
else
|
||||
snprintf(err, err_size, "missing comma after fetch keyword '%s'.", fkw);
|
||||
goto out_error;
|
||||
}
|
||||
|
||||
conv = find_sample_conv(str[*idx], endw - str[*idx]);
|
||||
if (!conv)
|
||||
break;
|
||||
while (*endt == ',') /* then trailing commas */
|
||||
endt++;
|
||||
|
||||
if (conv->in_type >= SMP_TYPES ||
|
||||
conv->out_type >= SMP_TYPES) {
|
||||
p = my_strndup(str[*idx], endw - str[*idx]);
|
||||
if (p) {
|
||||
snprintf(err, err_size, "returns type of conv method '%s' is unknown.", p);
|
||||
free(p);
|
||||
begw = endt; /* start of conv keyword */
|
||||
|
||||
if (!*begw) {
|
||||
/* none ? skip to next string */
|
||||
(*idx)++;
|
||||
begw = str[*idx];
|
||||
if (!begw || !*begw)
|
||||
break;
|
||||
}
|
||||
|
||||
for (endw = begw; *endw && *endw != '(' && *endw != ','; endw++);
|
||||
|
||||
free(ckw);
|
||||
ckw = my_strndup(begw, endw - begw);
|
||||
|
||||
conv = find_sample_conv(begw, endw - begw);
|
||||
if (!conv) {
|
||||
/* we found an isolated keyword that we don't know, it's not ours */
|
||||
if (begw == str[*idx])
|
||||
break;
|
||||
snprintf(err, err_size, "unknown conv method '%s'.", ckw);
|
||||
goto out_error;
|
||||
}
|
||||
|
||||
endt = endw;
|
||||
if (*endt == '(') {
|
||||
/* look for the end of this term */
|
||||
while (*endt && *endt != ')')
|
||||
endt++;
|
||||
if (*endt != ')') {
|
||||
snprintf(err, err_size, "syntax error: missing ')' after conv keyword '%s'.", ckw);
|
||||
goto out_error;
|
||||
}
|
||||
}
|
||||
|
||||
if (conv->in_type >= SMP_TYPES || conv->out_type >= SMP_TYPES) {
|
||||
snprintf(err, err_size, "returns type of conv method '%s' is unknown.", ckw);
|
||||
goto out_error;
|
||||
}
|
||||
|
||||
/* If impossible type conversion */
|
||||
if (!sample_casts[prev_type][conv->in_type]) {
|
||||
p = my_strndup(str[*idx], endw - str[*idx]);
|
||||
if (p) {
|
||||
snprintf(err, err_size, "conv method '%s' cannot be applied.", p);
|
||||
free(p);
|
||||
}
|
||||
snprintf(err, err_size, "conv method '%s' cannot be applied.", ckw);
|
||||
goto out_error;
|
||||
}
|
||||
|
||||
@ -687,28 +709,19 @@ struct sample_expr *sample_parse_expr(char **str, int *idx, char *err, int err_s
|
||||
LIST_ADDQ(&(expr->conv_exprs), &(conv_expr->list));
|
||||
conv_expr->conv = conv;
|
||||
|
||||
if (end != endw) {
|
||||
if (endt != endw) {
|
||||
char *err_msg = NULL;
|
||||
int err_arg;
|
||||
|
||||
if (!conv->arg_mask) {
|
||||
p = my_strndup(str[*idx], endw - str[*idx]);
|
||||
|
||||
if (p) {
|
||||
snprintf(err, err_size, "conv method '%s' does not support any args.", p);
|
||||
free(p);
|
||||
}
|
||||
snprintf(err, err_size, "conv method '%s' does not support any args.", ckw);
|
||||
goto out_error;
|
||||
}
|
||||
|
||||
al->kw = expr->fetch->kw;
|
||||
al->conv = conv_expr->conv->kw;
|
||||
if (make_arg_list(endw + 1, end - endw - 2, conv->arg_mask, &conv_expr->arg_p, &err_msg, NULL, &err_arg, al) < 0) {
|
||||
p = my_strndup(str[*idx], endw - str[*idx]);
|
||||
if (p) {
|
||||
snprintf(err, err_size, "invalid arg %d in conv method '%s' : %s.", err_arg+1, p, err_msg);
|
||||
free(p);
|
||||
}
|
||||
if (make_arg_list(endw + 1, endt - endw - 1, conv->arg_mask, &conv_expr->arg_p, &err_msg, NULL, &err_arg, al) < 0) {
|
||||
snprintf(err, err_size, "invalid arg %d in conv method '%s' : %s.", err_arg+1, ckw, err_msg);
|
||||
free(err_msg);
|
||||
goto out_error;
|
||||
}
|
||||
@ -717,31 +730,26 @@ struct sample_expr *sample_parse_expr(char **str, int *idx, char *err, int err_s
|
||||
conv_expr->arg_p = empty_arg_list;
|
||||
|
||||
if (conv->val_args && !conv->val_args(conv_expr->arg_p, &err_msg)) {
|
||||
p = my_strndup(str[*idx], endw - str[*idx]);
|
||||
if (p) {
|
||||
snprintf(err, err_size, "invalid args in conv method '%s' : %s.", p, err_msg);
|
||||
free(p);
|
||||
}
|
||||
snprintf(err, err_size, "invalid args in conv method '%s' : %s.", ckw, err_msg);
|
||||
free(err_msg);
|
||||
goto out_error;
|
||||
}
|
||||
}
|
||||
else if (conv->arg_mask) {
|
||||
p = my_strndup(str[*idx], endw - str[*idx]);
|
||||
if (p) {
|
||||
snprintf(err, err_size, "missing args for conv method '%s'.", p);
|
||||
free(p);
|
||||
}
|
||||
else if (ARGM(conv->arg_mask)) {
|
||||
snprintf(err, err_size, "missing args for conv method '%s'.", ckw);
|
||||
goto out_error;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
out:
|
||||
free(fkw);
|
||||
free(ckw);
|
||||
return expr;
|
||||
|
||||
out_error:
|
||||
/* TODO: prune_sample_expr(expr); */
|
||||
return NULL;
|
||||
expr = NULL;
|
||||
goto out;
|
||||
}
|
||||
|
||||
/*
|
||||
|
Loading…
Reference in New Issue
Block a user