]> git.ipfire.org Git - thirdparty/haproxy.git/commitdiff
MINOR: sample: add a new "concat" converter
authorWilly Tarreau <w@1wt.eu>
Mon, 19 Feb 2018 14:34:12 +0000 (15:34 +0100)
committerWilly Tarreau <w@1wt.eu>
Mon, 19 Feb 2018 14:34:12 +0000 (15:34 +0100)
It's always a pain not to be able to combine variables. This commit
introduces the "concat" converter, which appends a delimiter, a variable's
contents and another delimiter to an existing string. The result is a string.
This makes it easier to build composite variables made of other variables.

doc/configuration.txt
src/sample.c

index 51a1493d802044c9284341f35e2232a2b8bb7221..14d7063c9fa00e7b4c43780f3dbdbba94294ba35 100644 (file)
@@ -12810,6 +12810,27 @@ bytes(<offset>[,<length>])
   sample starting at an offset (in bytes) of the original sample and
   optionally truncated at the given length.
 
+concat([<start>],[<var>],[<end>])
+  Concatenates up to 3 fields after the current sample which is then turned to
+  a string. The first one, <start>, is a constant string, that will be appended
+  immediately after the existing sample. It may be omitted if not used. 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
+  <first> part. If the variable is not found, nothing is appended. It may be
+  omitted as well. The third field, <end> is a constant string that will be
+  appended after the variable. It may also be omitted. Together, these elements
+  allow to concatenate variables with delimiters to an existing set of
+  variables. This can be used to build new variables made of a succession of
+  other variables, such as colon-delimited varlues. Note that due to the config
+  parser, it is not possible to use a comma nor a closing parenthesis as
+  delimitors.
+
+  Example:
+    tcp-request session set-var(sess.src) src
+    tcp-request session set-var(sess.dn)  ssl_c_s_dn
+    tcp-request session set-var(txn.sig) str(),concat(<ip=,sess.ip,>),concat(<dn=,sess.dn,>)
+    http-request set-header x-hap-sig %[var(txn.sig)]
+
 cpl
   Takes the input value of type signed integer, applies a ones-complement
   (flips all bits) and returns the result as an signed integer.
index 655e7342690d9616757a265a4862846eabdab8c5..3e1a156f0e9e4d9f5b9910cf0681d4f1803d7303 100644 (file)
@@ -2531,6 +2531,83 @@ static int sample_conv_arith_even(const struct arg *arg_p,
        return 1;
 }
 
+/* appends an optional const string, an optional variable contents and another
+ * optional const string to an existing string.
+ */
+static int sample_conv_concat(const struct arg *arg_p, struct sample *smp, void *private)
+{
+       struct chunk *trash;
+       struct sample tmp;
+       int max;
+
+       trash = get_trash_chunk();
+       trash->len = smp->data.u.str.len;
+       if (trash->len > trash->size - 1)
+               trash->len = trash->size - 1;
+
+       memcpy(trash->str, smp->data.u.str.str, trash->len);
+       trash->str[trash->len] = 0;
+
+       /* append first string */
+       max = arg_p[0].data.str.len;
+       if (max > trash->size - 1 - trash->len)
+               max = trash->size - 1 - trash->len;
+
+       if (max) {
+               memcpy(trash->str + trash->len, arg_p[0].data.str.str, max);
+               trash->len += max;
+               trash->str[trash->len] = 0;
+       }
+
+       /* append second string (variable) if it's found and we can turn it
+        * into a string.
+        */
+       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) &&
+           (sample_casts[tmp.data.type][SMP_T_STR] == c_none ||
+            sample_casts[tmp.data.type][SMP_T_STR](&tmp))) {
+
+               max = tmp.data.u.str.len;
+               if (max > trash->size - 1 - trash->len)
+                       max = trash->size - 1 - trash->len;
+
+               if (max) {
+                       memcpy(trash->str + trash->len, tmp.data.u.str.str, max);
+                       trash->len += max;
+                       trash->str[trash->len] = 0;
+               }
+       }
+
+       /* append third string */
+       max = arg_p[2].data.str.len;
+       if (max > trash->size - 1 - trash->len)
+               max = trash->size - 1 - trash->len;
+
+       if (max) {
+               memcpy(trash->str + trash->len, arg_p[2].data.str.str, max);
+               trash->len += max;
+               trash->str[trash->len] = 0;
+       }
+
+       smp->data.u.str = *trash;
+       smp->data.type = SMP_T_STR;
+       return 1;
+}
+
+/* This function checks the "concat" converter's arguments and extracts the
+ * variable name and its scope.
+ */
+static int smp_check_concat(struct arg *args, struct sample_conv *conv,
+                           const char *file, int line, char **err)
+{
+       /* Try to decode a variable. */
+       if (args[1].data.str.len > 0 && !vars_check_arg(&args[1], NULL)) {
+               memprintf(err, "failed to register variable name '%s'", args[1].data.str.str);
+               return 0;
+       }
+       return 1;
+}
+
 /************************************************************************/
 /*       All supported sample fetch functions must be declared here     */
 /************************************************************************/
@@ -2539,6 +2616,9 @@ static int sample_conv_arith_even(const struct arg *arg_p,
 static int
 smp_fetch_true(const struct arg *args, struct sample *smp, const char *kw, void *private)
 {
+       if (!smp_make_rw(smp))
+               return 0;
+
        smp->data.type = SMP_T_BOOL;
        smp->data.u.sint = 1;
        return 1;
@@ -2841,6 +2921,7 @@ static struct sample_conv_kw_list sample_conv_kws = {ILH, {
        { "word",   sample_conv_word,      ARG2(2,SINT,STR), sample_conv_field_check, SMP_T_STR,  SMP_T_STR },
        { "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 },
 
        { "and",    sample_conv_binary_and, ARG1(1,STR), check_operator, SMP_T_SINT, SMP_T_SINT  },
        { "or",     sample_conv_binary_or,  ARG1(1,STR), check_operator, SMP_T_SINT, SMP_T_SINT  },