diff --git a/doc/configuration.txt b/doc/configuration.txt index 85d94d985..9a6202ded 100644 --- a/doc/configuration.txt +++ b/doc/configuration.txt @@ -39,8 +39,9 @@ Summary 2. Configuring HAProxy 2.1. Configuration file format -2.2. Time format -2.3. Examples +2.2. Quoting and escaping +2.3. Time format +2.4. Examples 3. Global parameters 3.1. Process management and security @@ -366,12 +367,61 @@ HAProxy's configuration process involves 3 major sources of parameters : The configuration file syntax consists in lines beginning with a keyword referenced in this manual, optionally followed by one or several parameters -delimited by spaces. If spaces have to be entered in strings, then they must be -preceded by a backslash ('\') to be escaped. Backslashes also have to be -escaped by doubling them. +delimited by spaces. -2.2. Time format +2.2. Quoting and escaping +------------------------- + +HAProxy's configuration introduces a quoting and escaping system similar to +many programming languages. The configuration file supports 3 types: escaping +with a backslash, weak quoting with double quotes, and strong quoting with +single quotes. + +If spaces have to be entered in strings, then they must be escaped by preceding +them by a backslash ('\') or by quoting them. Backslashes also have to be +escaped by doubling or strong quoting them. + +Escaping is achieved by preceding a special character by a backslash ('\'): + + \ to mark a space and differentiate it from a delimiter + \# to mark a hash and differentiate it from a comment + \\ to use a backslash + \' to use a single quote and differentiate it from strong quoting + \" to use a double quote and differentiate it from weak quoting + +Weak quoting is achieved by using double quotes (""). Weak quoting prevents +the interpretation of: + + space as a parameter separator + ' single quote as a strong quoting delimiter + # hash as a comment start + +But interpretation of escaping and special characters are not prevented by weak +quoting. + +Strong quoting is achieved by using single quotes (''). Inside single quotes, +nothing is interpreted, it's the efficient way to quote regexes. + +Quoted and escaped strings are replaced in memory by their interpreted +equivalent, it allows you to perform concatenation. + + Example: + # those are equivalents: + log-format %{+Q}o\ %t\ %s\ %{-Q}r + log-format "%{+Q}o %t %s %{-Q}r" + log-format '%{+Q}o %t %s %{-Q}r' + log-format "%{+Q}o %t"' %s %{-Q}r' + log-format "%{+Q}o %t"' %s'\ %{-Q}r + + # those are equivalents: + reqrep "^([^\ :]*)\ /static/(.*)" \1\ /\2 + reqrep "^([^ :]*)\ /static/(.*)" '\1 /\2' + reqrep "^([^ :]*)\ /static/(.*)" "\1 /\2" + reqrep "^([^ :]*)\ /static/(.*)" "\1\ /\2" + + +2.3. Time format ---------------- Some parameters involve values representing time, such as timeouts. These @@ -388,7 +438,7 @@ for every keyword. Supported units are : - d : days. 1d = 24h = 1440m = 86400s = 86400000ms -2.3. Examples +2.4. Examples ------------- # Simple configuration for an HTTP proxy listening on port 80 on all diff --git a/src/cfgparse.c b/src/cfgparse.c index 4d0a91a1e..96ee79873 100644 --- a/src/cfgparse.c +++ b/src/cfgparse.c @@ -6207,6 +6207,8 @@ int readcfgfile(const char *file) char *end; char *args[MAX_LINE_ARGS + 1]; char *line = thisline; + int dquote = 0; /* double quote */ + int squote = 0; /* simple quote */ linenum++; @@ -6224,15 +6226,31 @@ int readcfgfile(const char *file) /* skip leading spaces */ while (isspace((unsigned char)*line)) line++; - + arg = 0; args[arg] = line; while (*line && arg < MAX_LINE_ARGS) { + if (*line == '"' && !squote) { /* double quote outside single quotes */ + if (dquote) + dquote = 0; + else + dquote = 1; + memmove(line, line + 1, end - (line + 1)); + end--; + } + else if (*line == '\'' && !dquote) { /* single quote outside double quotes */ + if (squote) + squote = 0; + else + squote = 1; + memmove(line, line + 1, end - (line + 1)); + end--; + } + else if (*line == '\\' && !squote) { /* first, we'll replace \\, \, \#, \r, \n, \t, \xXX with their * C equivalent value. Other combinations left unchanged (eg: \1). */ - if (*line == '\\') { int skip = 0; if (line[1] == ' ' || line[1] == '\\' || line[1] == '#') { *line = line[1]; @@ -6241,7 +6259,7 @@ int readcfgfile(const char *file) else if (line[1] == 'r') { *line = '\r'; skip = 1; - } + } else if (line[1] == 'n') { *line = '\n'; skip = 1; @@ -6264,6 +6282,12 @@ int readcfgfile(const char *file) Alert("parsing [%s:%d] : invalid or incomplete '\\x' sequence in '%s'.\n", file, linenum, args[0]); err_code |= ERR_ALERT | ERR_FATAL; } + } else if (line[1] == '"') { + *line = '"'; + skip = 1; + } else if (line[1] == '\'') { + *line = '\''; + skip = 1; } if (skip) { memmove(line + 1, line + 1 + skip, end - (line + skip)); @@ -6271,12 +6295,12 @@ int readcfgfile(const char *file) } line++; } - else if (*line == '#' || *line == '\n' || *line == '\r') { + else if ((!squote && !dquote && *line == '#') || *line == '\n' || *line == '\r') { /* end of string, end of loop */ *line = 0; break; } - else if (isspace((unsigned char)*line)) { + else if (!squote && !dquote && isspace((unsigned char)*line)) { /* a non-escaped space is an argument separator */ *line++ = '\0'; while (isspace((unsigned char)*line)) @@ -6287,6 +6311,15 @@ int readcfgfile(const char *file) line++; } } + if (dquote) { + Alert("parsing [%s:%d] : Mismatched double quotes.\n", file, linenum); + err_code |= ERR_ALERT | ERR_FATAL; + } + + if (squote) { + Alert("parsing [%s:%d] : Mismatched simple quotes.\n", file, linenum); + err_code |= ERR_ALERT | ERR_FATAL; + } /* empty line */ if (!**args)