Hello community,
This is my first try contributing to the project and I'm looking forward to your feedback regarding the fix/new feature in #1621. There is one thing I'm not sure I understood correctly. According to this : - appends the contents of an optional variable - optionally appends a suffix both variable and suffix are optional so the following : - appends a configurable delimiter if the input text is not empty and neither the variable nor the suffix are empty didn't make sense to me. Shouldn't it be : - appends a configurable delimiter if the input text is not empty and either the variable or the suffix is not empty ? That logic was implemented in this patch. Also, I see some converters have corresponding varnish test files and I'm not sure whether that must be included in the patch or not. Best regards, Nikola Sale
From df559401e4495fd0200dd627e5b4344660907196 Mon Sep 17 00:00:00 2001 From: Nikola Sale <[email protected]> Date: Thu, 31 Mar 2022 23:14:41 +0200 Subject: [PATCH] MINOR: sample: converter: Add add_item converter This new converter is similar to the concat converter and can be used to build new variables made of a succession of other variables but the main difference is that it does the checks if adding a delimiter makes sense as wouldn't be the case e.g. if the current sample is empty or if variable we want to add is empty. Those situations would require 2 separate rules using concat converter where the first rule would have to check if the current sample string or variable is empty before adding a delimiter. --- doc/configuration.txt | 28 ++++++++++++++++ src/sample.c | 75 +++++++++++++++++++++++++++++++++++++++++++ 2 files changed, 103 insertions(+) diff --git a/doc/configuration.txt b/doc/configuration.txt index cb05fef91..5d559445c 100644 --- a/doc/configuration.txt +++ b/doc/configuration.txt @@ -16401,6 +16401,34 @@ concat([<start>],[<var>],[<end>]) tcp-request session set-var-fmt(txn.ipport) "addr=(%[sess.ip],%[sess.port])" ## does the same http-request set-header x-hap-sig %[var(txn.sig)] +add_item(<delim>,[<var>][,<suff>]]) + Concatenates a minimum 2 and up to 3 fields after the current sample which is + then turned into a string. The first one, [<delim>], is a constant string, that + will be appended immediately after the existing sample if an existing sample is + not empty and either the <var> or the <suff> is not empty. The second one, <var>, + is a variable name. The variable will be looked up, its contents converted to a + string, and it will be appended immediately after the <delim> part. If the variable + is not found, nothing is appended. It may be omitted but then the <suff> must be + supplied. The third field, <suff> is a constant string that will be appended after + the variable. It may also be omitted but only if the <var> is supplied. This + converter is similar to the concat converter and can be used to build new variables + made of a succession of other variables but the main difference is that it does + the checks if adding a delimiter makes sense as wouldn't be the case e.g. if the + current sample is empty. That situation would require 2 separate rules using concat + converter where the first rule would have to check if the current sample string is + empty before adding a delimiter. If commas or closing parenthesis are needed as + delimiters, they must be protected by quotes or backslashes, themselves protected + so that they are not stripped by the first level parser. See examples below. + + Examples: + http-request set-var(req.tagged) 'var(req.tagged),add_item(",",req.score1,"(site1)") if src,in_table(site1)' + http-request set-var(req.tagged) 'var(req.tagged),add_item(",",req.score2,"(site2)") if src,in_table(site2)' + http-request set-var(req.tagged) 'var(req.tagged),add_item(",",req.score3,"(site3)") if src,in_table(site3)' + http-request set-header x-tagged %[var(req.tagged)] + + http-request set-var(req.tagged) 'var(req.tagged),add_item(",",req.score1),add_item(",",req.score2)' + http-request set-var(req.tagged) 'var(req.tagged),add_item(",",,(site1))' if src,in_table(site1) + cpl Takes the input value of type signed integer, applies a ones-complement (flips all bits) and returns the result as an signed integer. diff --git a/src/sample.c b/src/sample.c index 766e87ad9..854c2d9dd 100644 --- a/src/sample.c +++ b/src/sample.c @@ -3122,6 +3122,80 @@ static int smp_check_concat(struct arg *args, struct sample_conv *conv, return 1; } +/* appends delimiter (only to a non empty input) followed by the + * variable contents concatenated with the optional sufix. + */ +static int sample_conv_add_item(const struct arg *arg_p, struct sample *smp, void *private) +{ + struct buffer *trash; + struct sample tmp; + int max, v_flag; + + trash = alloc_trash_chunk(); + if (!trash) + return 0; + + trash->data = smp->data.u.str.data; + if (trash->data > trash->size - 1) + trash->data = trash->size - 1; + + memcpy(trash->area, smp->data.u.str.area, trash->data); + trash->area[trash->data] = 0; + + /* check if variable is found and we can turn into a string.*/ + v_flag = 0; + smp_set_owner(&tmp, smp->px, smp->sess, smp->strm, smp->opt); + if (arg_p[1].type == ARGT_VAR && vars_get_by_desc(&arg_p[1].data.var, &tmp, NULL) && + (sample_casts[tmp.data.type][SMP_T_STR] == c_none || + sample_casts[tmp.data.type][SMP_T_STR](&tmp))) + v_flag = 1; + + /* append delimiter only if input is not empty and either + * the variable or the suffix are not empty + */ + if (smp->data.u.str.data && ((v_flag && tmp.data.u.str.data) || arg_p[2].data.str.data)) { + max = arg_p[0].data.str.data; + if (max > trash->size - 1 - trash->data) + max = trash->size - 1 - trash->data; + + if (max) { + memcpy(trash->area + trash->data, arg_p[0].data.str.area, max); + trash->data += max; + trash->area[trash->data] = 0; + } + } + + /* append variable contents if variable is found and turned into string*/ + if (v_flag) { + max = tmp.data.u.str.data; + if (max > trash->size - 1 - trash->data) + max = trash->size - 1 - trash->data; + + if (max) { + memcpy(trash->area + trash->data, tmp.data.u.str.area, max); + trash->data += max; + trash->area[trash->data] = 0; + } + } + + /* append optional suffix */ + max = arg_p[2].data.str.data; + if (max > trash->size - 1 - trash->data) + max = trash->size - 1 - trash->data; + + if (max) { + memcpy(trash->area + trash->data, arg_p[2].data.str.area, max); + trash->data += max; + trash->area[trash->data] = 0; + } + + smp->data.u.str = *trash; + smp->data.type = SMP_T_STR; + smp_dup(smp); + free_trash_chunk(trash); + return 1; +} + /* Compares string with a variable containing a string. Return value * is compatible with strcmp(3)'s return value. */ @@ -4236,6 +4310,7 @@ static struct sample_conv_kw_list sample_conv_kws = {ILH, { { "regsub", sample_conv_regsub, ARG3(2,REG,STR,STR), sample_conv_regsub_check, SMP_T_STR, SMP_T_STR }, { "sha1", sample_conv_sha1, 0, NULL, SMP_T_BIN, SMP_T_BIN }, { "concat", sample_conv_concat, ARG3(1,STR,STR,STR), smp_check_concat, SMP_T_STR, SMP_T_STR }, + { "add_item",sample_conv_add_item, ARG3(2,STR,STR,STR), smp_check_concat, SMP_T_STR, SMP_T_STR }, { "strcmp", sample_conv_strcmp, ARG1(1,STR), smp_check_strcmp, SMP_T_STR, SMP_T_SINT }, /* gRPC converters. */ -- 2.17.1

