Re: [PATCH] MINOR: sample: add json_string

2021-04-14 Thread Aleksandar Lazic

On 14.04.21 18:41, Tim Düsterhus wrote:

Aleks,

On 4/14/21 1:19 PM, Aleksandar Lazic wrote:

From 46ddac8379324b645c662e19de39d5de4ac74a77 Mon Sep 17 00:00:00 2001
From: Aleksandar Lazic 
Date: Wed, 14 Apr 2021 13:11:26 +0200
Subject: [PATCH 2/2] MINOR: sample: converter: Add json_query converter

With the json_query can a JSON value be extacted from a Header
or body of the request and saved to a variable.

This converter makes it possible to handle some JSON Workload
to route requests to differnt backends.


Typo: different.


---
 doc/configuration.txt  |  32 
 reg-tests/converter/json_query.vtc | 116 +
 src/sample.c   |  95 +++
 3 files changed, 243 insertions(+)
 create mode 100644 reg-tests/converter/json_query.vtc

diff --git a/doc/configuration.txt b/doc/configuration.txt
index f242300e7..374e7939b 100644
--- a/doc/configuration.txt
+++ b/doc/configuration.txt
@@ -15961,6 +15961,38 @@ json([])
   Output log:
  {"ip":"127.0.0.1","user-agent":"Very \"Ugly\" UA 1\/2"}

+


This empty line should not be there.


+json_query(,[])
+  This converter searches for the key given by  and returns
+  the value.
+   must be a valid JSONPath String as defined in


I'd use string in lowercase.


+  https://datatracker.ietf.org/doc/draft-ietf-jsonpath-base/
+
+  A floating point value will always be returned as String.
+
+  The follwing JSON types are recognized.


Typo: following.
I'd also use a ':' instead of '.'.


+   - string  : This is the default search type and returns a String;
+   - boolean : If the JSON value is not a String or a Number
+   - number  : When the JSON value is a Number then will the value be
+   converted to a String. If its known that the value is a
+   integer then add 'int' to the  which helps
+   haproxy to convert the value to a integer for further usage;


I'd probably completely rephrase this as:

The json_query converter supports the JSON types string, boolean and number. 
Floating point numbers will be returned as a string. By specifying the 
output_type 'int' the value will be converted to an Integer. If conversion is 
not possible the json_query converter fails.


Well I would like to here also some other opinions about the wording.


+  Example:
+ # get the value of the key 'iss' from a JWT Bearer token
+ http-request set-var(txn.token_payload) 
req.hdr(Authorization),word(2,.),ub64dec,json_query('$.iss')
+
+ # get a integer value from the request body
+ # "{"integer":4}" => 5
+ http-request set-var(txn.pay_int) 
req.body,json_query('$.integer','int'),add(1)
+
+ # get a key with '.' in the name
+ # {"my.key":"myvalue"} => myvalue
+ http-request set-var(txn.pay_mykey) req.body,json_query('$.my\\.key')
+
+ # {"boolean-false":false} => 0
+ http-request set-var(txn.pay_boolean_false) 
req.body,json_query('$.boolean-false')


These examples look good to me. I'd just move the JWT example to the bottom, so 
that the simple examples come first.


I prefer to keep it like this.



 language([,])
   Returns the value with the highest q-factor from a list as extracted from the
   "accept-language" header using "req.fhdr". Values with no q-factor have a
diff --git a/reg-tests/converter/json_query.vtc 
b/reg-tests/converter/json_query.vtc
new file mode 100644
index 0..88ef58a0c
--- /dev/null
+++ b/reg-tests/converter/json_query.vtc
@@ -0,0 +1,116 @@
+varnishtest "JSON Query converters Test"
+#REQUIRE_VERSION=2.4
+
+feature ignore_unknown_macro
+
+server s1 {
+    rxreq
+    txresp
+
+    rxreq
+    txresp
+
+    rxreq
+    txresp
+
+ rxreq
+ txresp
+
+ rxreq
+ txresp
+
+ rxreq
+ txresp
+ + rxreq
+ txresp
+ + rxreq
+ txresp
+} -start


You can use '-repeat 8' to simplify the server definition.


Good hint, thanks.


+haproxy h1 -conf {
+    defaults
+    mode http
+    timeout connect 1s
+    timeout client  1s
+    timeout server  1s
+    option http-buffer-request
+
+    frontend fe
+    bind "fd@${fe}"
+    tcp-request inspect-delay 1s
+
+    http-request set-var(sess.header_json) 
req.hdr(Authorization),json_query('$.iss')
+ http-request set-var(sess.pay_json) req.body,json_query('$.iss')
+ http-request set-var(sess.pay_int) 
req.body,json_query('$.integer',"int"),add(1)
+ http-request set-var(sess.pay_neg_int) 
req.body,json_query('$.negativ-integer',"int"),add(1)


Inconsistent indentation here.


+    http-request set-var(sess.pay_double) req.body,json_query('$.double')
+ http-request set-var(sess.pay_boolean_true) 
req.body,json_query('$.boolean-true')
+ http-request set-var(sess.pay_boolean_false) 
req.body,json_query('$.boolean-false')
+ http-request set-var(sess.pay_mykey) req.body,json_query('$.my\\.key')
+
+    http-response set-header x-var_header %[var(sess.header_json)]
+    http-response set-header x-var_body %[var(sess.pay_json)]
+    

Re: [PATCH] MINOR: ist: Add `istclear(struct ist*)`

2021-04-14 Thread Willy Tarreau
On Wed, Apr 14, 2021 at 07:14:30PM +0200, Tim Duesterhus wrote:
> > You can pass a pointer on dst. In the normalizer, you can update its 
> > size. It is thus possible to use dst when calling 
> > http_replace_req_path() or http_replace_req_query().
> > 
> 
> I see, that makes sense to me. I thought of the size being the current length
> of the ist and did not think of simply resetting it to 0 before starting the
> processing.
> 
> Here's a preparation patch to the ist library, allowing this pattern to be
> cleanly implemented. I've already incorporated it into my local normalization
> branch, but I'd like to implement the remaining feedback before sending a new
> series for it.

I like it. I thought we already had something to reset an ist that we
could repurpose, but no, right now resets are done by assigning so there
was nothing to do that yet.

Now applied, thanks!
Willy



Re: [PATCH] fix cirrus-ci builds by installing "pcre" package

2021-04-14 Thread Tim Düsterhus

Willy,

On 4/14/21 7:00 PM, Илья Шипицин wrote:

something changed in freebsd packaging. we now need to install pcre
directly.


This one looks good to me.

Best regards
Tim Düsterhus



[PATCH] MINOR: ist: Add `istclear(struct ist*)`

2021-04-14 Thread Tim Duesterhus
Christopher,
Willy,

On 4/13/21 6:34 PM, Christopher Faulet wrote:
>> Thus I can't simply take a `struct ist*` for the destination, as an ist
>> cannot communicate the size of the underlying buffer. I could
>> technically take a `struct buffer`, but I'd still like the result to
>> reside in an ist, because this is what the HTX API expects.
> 
> Hum, I don't understand. If you create an ist using the trash buffer 
> this way:
> 
> struct ist dst = ist2(replace->area, replace->size);
> 
> You can pass a pointer on dst. In the normalizer, you can update its 
> size. It is thus possible to use dst when calling 
> http_replace_req_path() or http_replace_req_query().
> 

I see, that makes sense to me. I thought of the size being the current length
of the ist and did not think of simply resetting it to 0 before starting the
processing.

Here's a preparation patch to the ist library, allowing this pattern to be
cleanly implemented. I've already incorporated it into my local normalization
branch, but I'd like to implement the remaining feedback before sending a new
series for it.

So: Please already take this patch if you think it is good.

Best regards
Tim Düsterhus

Apply with `git am --scissors` to automatically cut the commit message.

-- >8 --
istclear allows one to easily reset an ist to zero-size, while preserving the
previous size, indicating the length of the underlying buffer.
---
 include/import/ist.h | 30 ++
 1 file changed, 30 insertions(+)

diff --git a/include/import/ist.h b/include/import/ist.h
index af9bbac3c..0dc3008f5 100644
--- a/include/import/ist.h
+++ b/include/import/ist.h
@@ -281,6 +281,36 @@ static inline struct ist isttrim(const struct ist ist, 
size_t size)
return ret;
 }
 
+/* Sets the  of the  to zero and returns the previous length.
+ *
+ * This function is meant to be used in functions that receive an ist 
containing
+ * the destination buffer and the buffer's size. The returned size must be 
stored
+ * to prevent an overflow of such a destination buffer.
+ *
+ * If you simply want to clear an ist and do not care about the previous length
+ * then you should use `isttrim(ist, 0)`.
+ *
+ * Example Usage (fill the complete buffer with 'x'):
+ *
+ * void my_func(struct ist* dst)
+ * {
+ * size_t dst_size = istclear(dst);
+ * size_t i;
+ *
+ * for (i = 0; i < dst_size; i++)
+ * *dst = __istappend(*dst, 'x');
+ * }
+ */
+__attribute__((warn_unused_result))
+static inline size_t istclear(struct ist* ist)
+{
+   size_t len = ist->len;
+
+   ist->len = 0;
+
+   return len;
+}
+
 /* trims string  to no more than -1 characters and ensures that a
  * zero is placed after  (possibly reduced by one) and before ,
  * unless  is already zero. The string is returned. This is mostly aimed
-- 
2.31.1




[PATCH] fix cirrus-ci builds by installing "pcre" package

2021-04-14 Thread Илья Шипицин
Hello,

something changed in freebsd packaging. we now need to install pcre
directly.

Ilya
From 406a5b8cfee330cc74c18f0eca1811195e7eff6d Mon Sep 17 00:00:00 2001
From: Ilya Shipitsin 
Date: Wed, 14 Apr 2021 21:47:34 +0500
Subject: [PATCH] CI: cirrus: install "pcre" package

it turned out that our cirrus-ci freebsd builds got broken because
of missing "pcre". Most probably it was installed earlier as a dependency.
let us install it directly.
---
 .cirrus.yml | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/.cirrus.yml b/.cirrus.yml
index 0ac21bf0d..fdabfdcdd 100644
--- a/.cirrus.yml
+++ b/.cirrus.yml
@@ -4,7 +4,7 @@ FreeBSD_task:
   image_family: freebsd-12-2
   only_if: $CIRRUS_BRANCH =~ 'master|next'
   install_script:
-- pkg update -f && pkg upgrade -y && pkg install -y openssl git gmake lua53 socat
+- pkg update -f && pkg upgrade -y && pkg install -y openssl git gmake lua53 socat pcre
   script:
 - git clone https://github.com/VTest/VTest.git ../vtest
 - make -C ../vtest
-- 
2.30.2



Re: [PATCH] MINOR: sample: add json_string

2021-04-14 Thread Tim Düsterhus

Aleks,

On 4/14/21 1:19 PM, Aleksandar Lazic wrote:

From 46ddac8379324b645c662e19de39d5de4ac74a77 Mon Sep 17 00:00:00 2001
From: Aleksandar Lazic 
Date: Wed, 14 Apr 2021 13:11:26 +0200
Subject: [PATCH 2/2] MINOR: sample: converter: Add json_query converter

With the json_query can a JSON value be extacted from a Header
or body of the request and saved to a variable.

This converter makes it possible to handle some JSON Workload
to route requests to differnt backends.


Typo: different.


---
 doc/configuration.txt  |  32 
 reg-tests/converter/json_query.vtc | 116 +
 src/sample.c   |  95 +++
 3 files changed, 243 insertions(+)
 create mode 100644 reg-tests/converter/json_query.vtc

diff --git a/doc/configuration.txt b/doc/configuration.txt
index f242300e7..374e7939b 100644
--- a/doc/configuration.txt
+++ b/doc/configuration.txt
@@ -15961,6 +15961,38 @@ json([])
   Output log:
  {"ip":"127.0.0.1","user-agent":"Very \"Ugly\" UA 1\/2"}
 
+


This empty line should not be there.


+json_query(,[])
+  This converter searches for the key given by  and returns
+  the value.
+   must be a valid JSONPath String as defined in


I'd use string in lowercase.


+  https://datatracker.ietf.org/doc/draft-ietf-jsonpath-base/
+
+  A floating point value will always be returned as String.
+
+  The follwing JSON types are recognized.


Typo: following.
I'd also use a ':' instead of '.'.


+   - string  : This is the default search type and returns a String;
+   - boolean : If the JSON value is not a String or a Number
+   - number  : When the JSON value is a Number then will the value be
+   converted to a String. If its known that the value is a
+   integer then add 'int' to the  which helps
+   haproxy to convert the value to a integer for further usage;


I'd probably completely rephrase this as:

The json_query converter supports the JSON types string, boolean and 
number. Floating point numbers will be returned as a string. By 
specifying the output_type 'int' the value will be converted to an 
Integer. If conversion is not possible the json_query converter fails.



+  Example:
+ # get the value of the key 'iss' from a JWT Bearer token
+ http-request set-var(txn.token_payload) 
req.hdr(Authorization),word(2,.),ub64dec,json_query('$.iss')
+
+ # get a integer value from the request body
+ # "{"integer":4}" => 5
+ http-request set-var(txn.pay_int) 
req.body,json_query('$.integer','int'),add(1)
+
+ # get a key with '.' in the name
+ # {"my.key":"myvalue"} => myvalue
+ http-request set-var(txn.pay_mykey) req.body,json_query('$.my\\.key')
+
+ # {"boolean-false":false} => 0
+ http-request set-var(txn.pay_boolean_false) 
req.body,json_query('$.boolean-false')


These examples look good to me. I'd just move the JWT example to the 
bottom, so that the simple examples come first.



 language([,])
   Returns the value with the highest q-factor from a list as extracted from the
   "accept-language" header using "req.fhdr". Values with no q-factor have a
diff --git a/reg-tests/converter/json_query.vtc 
b/reg-tests/converter/json_query.vtc
new file mode 100644
index 0..88ef58a0c
--- /dev/null
+++ b/reg-tests/converter/json_query.vtc
@@ -0,0 +1,116 @@
+varnishtest "JSON Query converters Test"
+#REQUIRE_VERSION=2.4
+
+feature ignore_unknown_macro
+
+server s1 {
+   rxreq
+   txresp
+
+   rxreq
+   txresp
+
+   rxreq
+   txresp
+
+   rxreq
+   txresp
+
+   rxreq
+   txresp
+
+   rxreq
+   txresp
+ 
+ 	rxreq

+   txresp
+ 
+ 	rxreq

+   txresp
+} -start


You can use '-repeat 8' to simplify the server definition.


+haproxy h1 -conf {
+defaults
+   mode http
+   timeout connect 1s
+   timeout client  1s
+   timeout server  1s
+   option http-buffer-request
+
+frontend fe
+   bind "fd@${fe}"
+   tcp-request inspect-delay 1s
+
+   http-request set-var(sess.header_json) 
req.hdr(Authorization),json_query('$.iss')
+   http-request set-var(sess.pay_json) req.body,json_query('$.iss')
+   http-request set-var(sess.pay_int) 
req.body,json_query('$.integer',"int"),add(1)
+http-request set-var(sess.pay_neg_int) 
req.body,json_query('$.negativ-integer',"int"),add(1)


Inconsistent indentation here.


+   http-request set-var(sess.pay_double) req.body,json_query('$.double')
+   http-request set-var(sess.pay_boolean_true) 
req.body,json_query('$.boolean-true')
+   http-request set-var(sess.pay_boolean_false) 
req.body,json_query('$.boolean-false')
+   http-request set-var(sess.pay_mykey) req.body,json_query('$.my\\.key')
+
+   http-response set-header x-var_header %[var(sess.header_json)]
+   http-response set-header x-var_body %[var(sess.pay_json)]
+   http-response set-header x-var_body_int %[var(sess.pay_int)]
+   http-response 

Re: Still 100% CPU usage in 2.3.9 & 2.2.13 (Was: Re: [2.2.9] 100% CPU usage)

2021-04-14 Thread Christopher Faulet

Le 10/04/2021 à 00:34, Robin H. Johnson a écrit :

On Fri, Apr 09, 2021 at 10:14:26PM +0200, Christopher Faulet wrote:

It seems you have a blocking call in one of your lua script. The threads dump
shows many threads blocked in hlua_ctx_init. Many others are executing lua.
Unfortunately, for a unknown reason, there is no stack traceback.

All of our Lua is string handling. Parsing headers, and then building
TXN variables as well as a set of applets that return responses in cases
where we don't want to go to a backend server as the response is simple
enough to generate inside the LB.


For the 2.3 and prior, the lua scripts are executed under a global lock. Thus
blocking calls in a lua script are awful, because it does not block only one
thread but all others too. I guess the same issue exists on the 1.8, but there
is no watchdog on this version. Thus, time to time HAProxy hangs and may report
huge latencies but, at the end it recovers and continues to process data. It is
exactly the purpose of the watchdog, reporting hidden bugs related to spinning
loops and deadlocks.

Nothing in this Lua code should have blocking calls at all.
The Lua code has zero calls to external services, no sockets, no sleeps,
no print, no Map.new (single call in the Lua startup, not inside any
applet or fetch), no usage of other packages, no file handling, no other
IO.

I'm hoping I can get $work to agree to fully open-source the Lua, so you
can see this fact and review the code to confirm that it SHOULD be
non-blocking.


I trust you on this point. If you are not using external component, it should 
indeed be ok. So, it is probably a contention issue on the global Lua lock. If 
you are able to generate and inspect a core file, it should help you to figure 
out what really happens.




However, I may be wrong. It may be just a contention problem because your are
executing lua with 64 threads and a huge workload. In this case, you may give a
try to the 2.4 (under development). There is a way to have a separate lua
context for each thread loading the scripts with "lua-load-per-thread"
directive. Out of curiosity, on the 1.8, are you running HAProxy with several
threads or are you spawning several processes?

nbthread=64, nbproc=1 on both 1.8/2.x


It is thus surprising, if it is really a contention issue, that you never 
observed slow down on the 1.8. There is no watchdog, but the thread 
implementation is a bit awkward on the 1.8. 2.X are better on this point, the 
best being the 2.4.



Yes, we're hoping to try 2.4.x, just working on some parts to get there.



--
Christopher Faulet



Re: [PATCH] MINOR: sample: add json_string

2021-04-14 Thread Aleksandar Lazic

Hi.

here now the current version of the patches.

Regards
Aleks.

On 14.04.21 10:45, Aleksandar Lazic wrote:

On 14.04.21 04:36, Willy Tarreau wrote:

On Wed, Apr 14, 2021 at 03:02:20AM +0200, Aleksandar Lazic wrote:

But then, could it make sense to also support "strict integers": values
that can accurately be represented as integers and which are within the
JSON valid range for integers (-2^52 to 2^52 with no decimal part) ?
This would then make the converter return nothing in case of violation
(i.e. risk of losing precision). This would also reject NaN and infinite
that the lib supports.


You mean the same check which is done in arith_add().


Not exactly because arith_add only checks for overflows after addition
and tries to cap the result, but I'd rather just say that if the decoded
number is <= -2^53 or >= 2^53 then the converter should return a no match
in case an integer was requested.


Okay got you.

There is such a check in stats.c which I copied to sample.c but this does
not look right.

Maybe I should create a include/haproxy/json-t.h and add the values there,
what do you think?


```
/* Limit JSON integer values to the range [-(2**53)+1, (2**53)-1] as per
* the recommendation for interoperable integers in section 6 of RFC 7159.
*/
#define JSON_INT_MAX ((1ULL << 53) - 1)
#define JSON_INT_MIN (0 - JSON_INT_MAX)

/* This sample function get the value from a given json string.
* The mjson library is used to parse the json struct
*/
static int sample_conv_json_query(const struct arg *args, struct sample *smp, 
void *private)
{
 struct buffer *trash = get_trash_chunk();
 const char *p; /* holds the temporay string from mjson_find */
 int tok, n;    /* holds the token enum and the length of the value */
 int rc;    /* holds the return code from mjson_get_string */

 tok = mjson_find(smp->data.u.str.area, smp->data.u.str.data, 
args[0].data.str.area, , );

 switch(tok) {
     case MJSON_TOK_NUMBER:
     if (args[1].type == ARGT_SINT) {
     smp->data.u.sint = atoll(p);

     if (smp->data.u.sint < JSON_INT_MIN || smp->data.u.sint > 
JSON_INT_MAX)
     return 0;

     smp->data.type = SMP_T_SINT;
     } else {
...
```


H that's not your fault but now I'm seeing that we already have a
converter inappropriately called "json", so we don't even know in which
direction it works by just looking at its name :-(  Same issue as for
base64.

May I suggest that you call yours "json_decode" or maybe shorter
"json_dec" so that it's more explicit that it's the decode one ? Because
for me "json_string" is the one that will emit a json string from some
input (which it is not). Then we could later create "json_enc" and warn
when "json" alone is used. Or even "jsdec" and "jsenc" which are much
shorter and still quite explicit.


How about "json_query" because it's exactly what it does :-)


I'm not familiar with the notion of "query" to decode and extract contents
but I'm not the most representative user and am aware of the "jq" command-
line utility that does this. So if it sounds natural to others I'm fine
with this.


I'm seeing that there's a very nice mjson_find() which does *exactly* what
you need:

 "In a JSON string s, len, find an element by its JSONPATH path. Save
  found element in tokptr, toklen. If not found, return JSON_TOK_INVALID.
  If found, return one of: MJSON_TOK_STRING, MJSON_TOK_NUMBER,
  MJSON_TOK_TRUE, MJSON_TOK_FALSE, MJSON_TOK_NULL, MJSON_TOK_ARRAY,
  MJSON_TOK_OBJECT.
  If a searched key contains ., [ or ] characters, they should be escaped
  by a backslash."

So you get the type in return. I think you can then call one of the
related functions depending on what is found, which is more reliable
than iterating over multiple attempts.


Oh yes, this sounds like a better approach.
I have now used this suggestion and I hope you can help me to fix the double 
parsing
issue or is it acceptable to parse the input twice?


 From what I've seen in the code in the lib you have no other option.
I thought it might be possible to call mjson_get_string() on the
resulting pointer but you would need to find a way to express that
you want to extract the immediate content, maybe by having an empty
key designation or something like this. This point is not clear to
me and the unit tests in the project all re-parse the input string
after mjson_find(), so probably this is the way to do it.


The check functions handles the int arg now as suggested.

```
/* This function checks the "json_query" converter's arguments. */
static int sample_check_json_query(struct arg *arg, struct sample_conv *conv,
    const char *file, int line, char **err)
{
if (arg[0].data.str.data == 0) { /* empty */
    memprintf(err, "json_path must not be empty");
    return 0;
}

if (arg[1].data.str.data != 0) {
    if (strncmp(arg[1].data.str.area, "int", 

[OT PATCH 2/2] MINOR: opentracing: transfer of context names without prefix

2021-04-14 Thread Miroslav Zagorac

Hello all,

In order to enable the assignment of a context name, and yet exclude the
use of that name (prefix in this case) when extracting the context from
the HTTP header, a special character '-' has been added, which can be
specified at the beginning of the prefix.

So let's say if we look at examples of the fe-be configuration, we can
transfer the context via an HTTP header without a prefix like this:

  fe/ot.cfg:
..
span "HAProxy session"
inject "" use-headers
event on-backend-http-request

Such a context can be read in another process using a name that has a
special '-' sign at the beginning:

  be/ot.cfg:
ot-scope frontend_http_request
extract "-ot-ctx" use-headers
span "HAProxy session" child-of "-ot-ctx" root
..

This means that the context name will be '-ot-ctx' but it will not be
used when extracting data from HTTP headers.

Of course, if the context does not have a prefix set, all HTTP headers
will be inserted into the OpenTracing library as context.  All of the
above will only work correctly if that library can figure out what is
relevant to the context and what is not.

Best regards,

--
Zaga

What can change the nature of a man?
>From 6ede4cf6e83b327db132e830dd09b728057ae375 Mon Sep 17 00:00:00 2001
From: Miroslav Zagorac 
Date: Wed, 14 Apr 2021 11:47:28 +0200
Subject: [PATCH 2/2] MINOR: opentracing: transfer of context names without
 prefix

In order to enable the assignment of a context name, and yet exclude the
use of that name (prefix in this case) when extracting the context from
the HTTP header, a special character '-' has been added, which can be
specified at the beginning of the prefix.

So let's say if we look at examples of the fe-be configuration, we can
transfer the context via an HTTP header without a prefix like this:

  fe/ot.cfg:
..
span "HAProxy session"
inject "" use-headers
event on-backend-http-request

Such a context can be read in another process using a name that has a
special '-' sign at the beginning:

  be/ot.cfg:
ot-scope frontend_http_request
extract "-ot-ctx" use-headers
span "HAProxy session" child-of "-ot-ctx" root
..

This means that the context name will be '-ot-ctx' but it will not be
used when extracting data from HTTP headers.

Of course, if the context does not have a prefix set, all HTTP headers
will be inserted into the OpenTracing library as context.  All of the
above will only work correctly if that library can figure out what is
relevant to the context and what is not.
---
 addons/ot/include/parser.h |  1 +
 addons/ot/src/http.c   | 13 +
 2 files changed, 14 insertions(+)

diff --git a/addons/ot/include/parser.h b/addons/ot/include/parser.h
index 5e7b298d5..75a39cc0c 100644
--- a/addons/ot/include/parser.h
+++ b/addons/ot/include/parser.h
@@ -38,6 +38,7 @@
 #define FLT_OT_PARSE_SPAN_REF_CHILD "child-of"
 #define FLT_OT_PARSE_SPAN_REF_FOLLOWS   "follows-from"
 #define FLT_OT_PARSE_CTX_AUTONAME   "-"
+#define FLT_OT_PARSE_CTX_IGNORE_NAME'-'
 #define FLT_OT_PARSE_CTX_USE_HEADERS"use-headers"
 #define FLT_OT_PARSE_CTX_USE_VARS   "use-vars"
 #define FLT_OT_PARSE_OPTION_HARDERR "hard-errors"
diff --git a/addons/ot/src/http.c b/addons/ot/src/http.c
index 72b31b76f..4a12ed854 100644
--- a/addons/ot/src/http.c
+++ b/addons/ot/src/http.c
@@ -99,6 +99,19 @@ struct otc_text_map *flt_ot_http_headers_get(struct channel *chn, const char *pr
 	if (chn == NULL)
 		FLT_OT_RETURN(retptr);
 
+	/*
+	 * The keyword 'inject' allows you to define the name of the OpenTracing
+	 * context without using a prefix.  In that case all HTTP headers are
+	 * transferred because it is not possible to separate them from the
+	 * OpenTracing context (this separation is usually done via a prefix).
+	 *
+	 * When using the 'extract' keyword, the context name must be specified.
+	 * To allow all HTTP headers to be extracted, the first character of
+	 * that name must be set to FLT_OT_PARSE_CTX_IGNORE_NAME.
+	 */
+	if (FLT_OT_STR_ISVALID(prefix) && (*prefix == FLT_OT_PARSE_CTX_IGNORE_NAME))
+		prefix_len = 0;
+
 	htx = htxbuf(&(chn->buf));
 
 	for (pos = htx_get_first(htx); pos != -1; pos = htx_get_next(htx, pos)) {
-- 
2.30.1



[OT PATCH 1/2] MINOR: opentracing: correct calculation of the number of arguments in the args[]

2021-04-14 Thread Miroslav Zagorac

Hello all,

It is possible that some arguments within the configuration line are not
specified; that is, they are set to a blank string.

For example:
  keyword '' arg_2

In that case the content of the args field will be like this:
  args[0]:  'keyword'
  args[1]:  NULL pointer
  args[2]:  'arg_2'
  args[3 .. MAX_LINE_ARGS): NULL pointers

The previous way of calculating the number of arguments (as soon as a
null pointer is encountered) could not place an argument on an empty
string.

All of the above is essential for passing the OpenTracing context via
the HTTP headers (keyword 'inject'), where one of the arguments is the
context name prefix.  This way we can set an empty prefix, which is very
useful if we get context from some other process that can't add a prefix
to that data; or we want to pass the context to some process that cannot
handle the prefix of that data.

Best regards,

--
Zaga

What can change the nature of a man?
>From 0c0c0c77fe5b1a71ea639b5693eab85a917e9df0 Mon Sep 17 00:00:00 2001
From: Miroslav Zagorac 
Date: Wed, 14 Apr 2021 11:44:58 +0200
Subject: [PATCH 1/2] MINOR: opentracing: correct calculation of the number of
 arguments in the args[]

It is possible that some arguments within the configuration line are not
specified; that is, they are set to a blank string.

For example:
  keyword '' arg_2

In that case the content of the args field will be like this:
  args[0]:  'keyword'
  args[1]:  NULL pointer
  args[2]:  'arg_2'
  args[3 .. MAX_LINE_ARGS): NULL pointers

The previous way of calculating the number of arguments (as soon as a
null pointer is encountered) could not place an argument on an empty
string.

All of the above is essential for passing the OpenTracing context via
the HTTP headers (keyword 'inject'), where one of the arguments is the
context name prefix.  This way we can set an empty prefix, which is very
useful if we get context from some other process that can't add a prefix
to that data; or we want to pass the context to some process that cannot
handle the prefix of that data.
---
 addons/ot/src/parser.c | 17 
 addons/ot/src/util.c   | 44 +++---
 2 files changed, 41 insertions(+), 20 deletions(-)

diff --git a/addons/ot/src/parser.c b/addons/ot/src/parser.c
index c7522e942..5dec8629d 100644
--- a/addons/ot/src/parser.c
+++ b/addons/ot/src/parser.c
@@ -189,7 +189,7 @@ static const char *flt_ot_parse_invalid_char(const char *name, int type)
  */
 static int flt_ot_parse_cfg_check(const char *file, int linenum, char **args, const void *id, const struct flt_ot_parse_data *parse_data, size_t parse_data_size, const struct flt_ot_parse_data **pdata, char **err)
 {
-	int i, retval = ERR_NONE;
+	int i, argc, retval = ERR_NONE;
 
 	FLT_OT_FUNC("\"%s\", %d, %p, %p, %p, %zu, %p:%p, %p:%p", file, linenum, args, id, parse_data, parse_data_size, FLT_OT_DPTR_ARGS(pdata), FLT_OT_DPTR_ARGS(err));
 
@@ -197,12 +197,15 @@ static int flt_ot_parse_cfg_check(const char *file, int linenum, char **args, co
 
 	*pdata = NULL;
 
+	/* First check here if args[0] is the correct keyword. */
 	for (i = 0; (*pdata == NULL) && (i < parse_data_size); i++)
 		if (strcmp(parse_data[i].name, args[0]) == 0)
 			*pdata = parse_data + i;
 
 	if (*pdata == NULL)
 		FLT_OT_PARSE_ERR(err, "'%s' : unknown keyword", args[0]);
+	else
+		argc = flt_ot_args_count(args);
 
 	if ((retval & ERR_CODE) || (id == NULL))
 		/* Do nothing. */;
@@ -214,20 +217,16 @@ static int flt_ot_parse_cfg_check(const char *file, int linenum, char **args, co
 	 * line than is required.
 	 */
 	if (!(retval & ERR_CODE))
-		for (i = 1; i < (*pdata)->args_min; i++)
-			if (!FLT_OT_ARG_ISVALID(i))
-FLT_OT_PARSE_ERR(err, "'%s' : too few arguments (use '%s%s')", args[0], (*pdata)->name, (*pdata)->usage);
+		if (argc < (*pdata)->args_min)
+			FLT_OT_PARSE_ERR(err, "'%s' : too few arguments (use '%s%s')", args[0], (*pdata)->name, (*pdata)->usage);
 
 	/*
 	 * Checking that more arguments are specified in the configuration
 	 * line than the maximum allowed.
 	 */
-	if (!(retval & ERR_CODE) && ((*pdata)->args_max > 0)) {
-		for ( ; (i <= (*pdata)->args_max) && FLT_OT_ARG_ISVALID(i); i++);
-
-		if (i > (*pdata)->args_max)
+	if (!(retval & ERR_CODE) && ((*pdata)->args_max > 0))
+		if (argc > (*pdata)->args_max)
 			FLT_OT_PARSE_ERR(err, "'%s' : too many arguments (use '%s%s')", args[0], (*pdata)->name, (*pdata)->usage);
-	}
 
 	/* Checking that the first argument has only allowed characters. */
 	if (!(retval & ERR_CODE) && ((*pdata)->check_name > 0)) {
diff --git a/addons/ot/src/util.c b/addons/ot/src/util.c
index 3adc5a300..f6812bcc2 100644
--- a/addons/ot/src/util.c
+++ b/addons/ot/src/util.c
@@ -37,13 +37,13 @@
  */
 void flt_ot_args_dump(char **args)
 {
-	int i, n;
+	int i, argc;
 
-	for (n = 1; FLT_OT_ARG_ISVALID(n); n++);
+	argc = flt_ot_args_count(args);
 
-	(void)fprintf(stderr, 

[OT PATCH 0/2] opentracing: use of the context name without prefix

2021-04-14 Thread Miroslav Zagorac

Hello all,

the next two patches allow you to transfer the OpenTracing context via 
an HTTP headers without using a name prefix.


This is very useful when transferring context from or to some other 
process that cannot add or remove a prefix from that data.


Best regards,

--
Zaga

What can change the nature of a man?



Memory allocation for haproxy

2021-04-14 Thread Robert Ionescu
Hi all,

I am working on a new patch for haproxy and would need the malloc method
used to allocate memory for structs like the connection struct in
connection-t.c
In case I want to expand the struct with a new variable, do I need to take
care of the memory for this variable or is there already a dynamic memory
allocation for the given struct?

Best regards
___
Robert Ionescu

*The information contained in this message is confidential and may be
legally privileged. The message is intended solely for the addressee(s). If
you are not the intended recipient, you are hereby notified that any use,
dissemination, or reproduction is strictly prohibited and may be unlawful.
If you are not the intended recipient, please contact the sender by return
e-mail and destroy all copies of the original message.*


Re: [PATCH] MINOR: sample: add json_string

2021-04-14 Thread Aleksandar Lazic

On 14.04.21 04:36, Willy Tarreau wrote:

On Wed, Apr 14, 2021 at 03:02:20AM +0200, Aleksandar Lazic wrote:

But then, could it make sense to also support "strict integers": values
that can accurately be represented as integers and which are within the
JSON valid range for integers (-2^52 to 2^52 with no decimal part) ?
This would then make the converter return nothing in case of violation
(i.e. risk of losing precision). This would also reject NaN and infinite
that the lib supports.


You mean the same check which is done in arith_add().


Not exactly because arith_add only checks for overflows after addition
and tries to cap the result, but I'd rather just say that if the decoded
number is <= -2^53 or >= 2^53 then the converter should return a no match
in case an integer was requested.


Okay got you.

There is such a check in stats.c which I copied to sample.c but this does
not look right.

Maybe I should create a include/haproxy/json-t.h and add the values there,
what do you think?


```
/* Limit JSON integer values to the range [-(2**53)+1, (2**53)-1] as per
 * the recommendation for interoperable integers in section 6 of RFC 7159.
 */
#define JSON_INT_MAX ((1ULL << 53) - 1)
#define JSON_INT_MIN (0 - JSON_INT_MAX)

/* This sample function get the value from a given json string.
 * The mjson library is used to parse the json struct
 */
static int sample_conv_json_query(const struct arg *args, struct sample *smp, 
void *private)
{
struct buffer *trash = get_trash_chunk();
const char *p; /* holds the temporay string from mjson_find */
int tok, n;/* holds the token enum and the length of the value */
int rc;/* holds the return code from mjson_get_string */

tok = mjson_find(smp->data.u.str.area, smp->data.u.str.data, 
args[0].data.str.area, , );

switch(tok) {
case MJSON_TOK_NUMBER:
if (args[1].type == ARGT_SINT) {
smp->data.u.sint = atoll(p);

if (smp->data.u.sint < JSON_INT_MIN || 
smp->data.u.sint > JSON_INT_MAX)
return 0;

smp->data.type = SMP_T_SINT;
} else {
...
```


H that's not your fault but now I'm seeing that we already have a
converter inappropriately called "json", so we don't even know in which
direction it works by just looking at its name :-(  Same issue as for
base64.

May I suggest that you call yours "json_decode" or maybe shorter
"json_dec" so that it's more explicit that it's the decode one ? Because
for me "json_string" is the one that will emit a json string from some
input (which it is not). Then we could later create "json_enc" and warn
when "json" alone is used. Or even "jsdec" and "jsenc" which are much
shorter and still quite explicit.


How about "json_query" because it's exactly what it does :-)


I'm not familiar with the notion of "query" to decode and extract contents
but I'm not the most representative user and am aware of the "jq" command-
line utility that does this. So if it sounds natural to others I'm fine
with this.


I'm seeing that there's a very nice mjson_find() which does *exactly* what
you need:

 "In a JSON string s, len, find an element by its JSONPATH path. Save
  found element in tokptr, toklen. If not found, return JSON_TOK_INVALID.
  If found, return one of: MJSON_TOK_STRING, MJSON_TOK_NUMBER,
  MJSON_TOK_TRUE, MJSON_TOK_FALSE, MJSON_TOK_NULL, MJSON_TOK_ARRAY,
  MJSON_TOK_OBJECT.
  If a searched key contains ., [ or ] characters, they should be escaped
  by a backslash."

So you get the type in return. I think you can then call one of the
related functions depending on what is found, which is more reliable
than iterating over multiple attempts.


Oh yes, this sounds like a better approach.
I have now used this suggestion and I hope you can help me to fix the double 
parsing
issue or is it acceptable to parse the input twice?


 From what I've seen in the code in the lib you have no other option.
I thought it might be possible to call mjson_get_string() on the
resulting pointer but you would need to find a way to express that
you want to extract the immediate content, maybe by having an empty
key designation or something like this. This point is not clear to
me and the unit tests in the project all re-parse the input string
after mjson_find(), so probably this is the way to do it.


The check functions handles the int arg now as suggested.

```
/* This function checks the "json_query" converter's arguments. */
static int sample_check_json_query(struct arg *arg, struct sample_conv *conv,
const char *file, int line, char **err)
{
if (arg[0].data.str.data == 0) { /* empty */
memprintf(err, "json_path must not be empty");
return 0;
}

if (arg[1].data.str.data != 0) {
  

Re: [PATCH] MINOR: sample: add json_string

2021-04-14 Thread Willy Tarreau
On Wed, Apr 14, 2021 at 09:05:53AM +0200, Tim Düsterhus wrote:
> Willy,
> 
> On 4/14/21 4:36 AM, Willy Tarreau wrote:
> > > How about "json_query" because it's exactly what it does :-)
> > 
> > I'm not familiar with the notion of "query" to decode and extract contents
> > but I'm not the most representative user and am aware of the "jq" command-
> > line utility that does this. So if it sounds natural to others I'm fine
> > with this.
> 
> +1 for json_query. It was among my suggestions for an improved name.
> 
> We are not decoding the full JSON into a data structure, we are querying the
> value of a single entry (as in jq or SQL). json_decode or something like
> that would be misleading.

OK, fine by me then.

Thanks,
Willy



Re: [PATCH] MINOR: sample: add json_string

2021-04-14 Thread Tim Düsterhus

Willy,

On 4/14/21 4:36 AM, Willy Tarreau wrote:

How about "json_query" because it's exactly what it does :-)


I'm not familiar with the notion of "query" to decode and extract contents
but I'm not the most representative user and am aware of the "jq" command-
line utility that does this. So if it sounds natural to others I'm fine
with this.


+1 for json_query. It was among my suggestions for an improved name.

We are not decoding the full JSON into a data structure, we are querying 
the value of a single entry (as in jq or SQL). json_decode or something 
like that would be misleading.


Best regards
Tim Düsterhus