From da1620b3175c63b768a8537951667885fef77e8c Mon Sep 17 00:00:00 2001 From: Willy Tarreau Date: Wed, 20 Nov 2024 08:44:39 +0100 Subject: [PATCH] DOC: configuration: explain quotes and spaces in conditional blocks Conditional blocks inherit the same tokenizer and argument parser as the rest of the configuration, but are also silently concatenated around groups of spaces and tabs. This can lead to subtle failures for configs containing spaces around commas and parenthesis, where a string comparison might silently fail for example. Let's better document this particular case. Thanks to Valentine for analysing and reporting the problem. This can be backported to 2.4. --- doc/configuration.txt | 48 +++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 48 insertions(+) diff --git a/doc/configuration.txt b/doc/configuration.txt index 645ccb3d27..0491099ca3 100644 --- a/doc/configuration.txt +++ b/doc/configuration.txt @@ -1056,6 +1056,54 @@ expression made of any combination of: - expressions combined with a logical OR ('||'), which will be evaluated from right to left until one returns true +The same line tokenizer and argument parser are used as for the rest of the +configuration language. Words are split around consecutive series of one or +more unquoted spaces or tabs, and are reassembled together using a single space +to delimit them before evaluation, in order to save the user from having to +quote the entire line. But this also means that spaces surrounding commas or +parenthesis are definitely part of the value, which is not always expected. +For example, the expression below: + + .if defined( HAPROXY_MWORKER ) + +will test for the existence of variable " HAPROXY_MWORKER " (with spaces), +and this one: + + .if streq("$ENABLE_SSL", 1) + +will compare the environment variable "ENABLE_SSL" to the value " 1" (with a +single leading space). The reason is the line is first split into words like +this: + + .if streq("$ENABLE_SSL", 1) + |---|--------------------| |--| + 1 2 3 + +then the weak quoting is applied and environment variable "$ENABLE_SSL" is +resolved (let's say for example that ENABLE_SSL=0), and finally the words are +reassembled into a single string by placing a single space between the words: + + .if streq(0, 1) + |---|-------|--| + 1 2 3 + +and only then it is parsed as a single expression. The space that was inserted +between the comma and "1" is still part of the argument value, making this +argument " 1": + + .if streq(0, 1) + |---|-----|-|--| + \ \ \ \_ argument2: " 1" + \ \ \___ argument1: "0" + \ \_______ function: "streq" + \___________ directive: ".if" + +It's visible here that even if ENABLE_SSL had been equal to "1", it wouldn't +have matched " 1" since the string would differ by one space. + +Note: as explained in section "2.2. Quoting and escaping", a good rule of thumb + is to never insert unneeded spaces inside expressions. + Note that like in other languages, the AND operator has precedence over the OR operator, so that "A && B || C && D" evalues as "(A && B) || (C && D)".