Form</a> (BNF) is a notation technique for context-free grammars,
often used to describe the syntax of languages used in computing.
In most cases, expressions are used to express boolean values.
- For these, the starting point in the BNF is <code>expr</code>.
- However, a few directives like <directive
- module="mod_log_debug">LogMessage</directive> accept expressions
+ For these, the starting point in the BNF is <code>cond</code>.
+ Directives like <directive module="core">ErrorDocument</directive>,
+ <directive module="mod_authz_core">Require</directive>,
+ <directive module="mod_authn_core">AuthName</directive>,
+ <directive module="mod_alias">Redirect</directive>,
+ <directive module="mod_headers">Header</directive>,
+ <directive module="mod_crypto">CryptoKey</directive> or
+ <directive module="mod_log_debug">LogMessage</directive> accept expressions
that evaluate to a string value. For those, the starting point in
the BNF is <code>string</code>.
</p>
<blockquote>
<pre>
-expr ::= "<strong>true</strong>" | "<strong>false</strong>"
- | "<strong>!</strong>" expr
- | expr "<strong>&&</strong>" expr
- | expr "<strong>||</strong>" expr
- | "<strong>(</strong>" expr "<strong>)</strong>"
+expr ::= cond
+ | string
+
+string ::= substring
+ | string substring
+
+cond ::= "<strong>true</strong>"
+ | "<strong>false</strong>"
+ | "<strong>!</strong>" cond
+ | cond "<strong>&&</strong>" cond
+ | cond "<strong>||</strong>" cond
| comp
+ | "<strong>(</strong>" cond "<strong>)</strong>"
comp ::= stringcomp
| integercomp
| unaryop word
| word binaryop word
- | word "<strong>in</strong>" "<strong>{</strong>" wordlist "<strong>}</strong>"
- | word "<strong>in</strong>" listfunction
+ | word "<strong>in</strong>" listfunc
| word "<strong>=~</strong>" regex
| word "<strong>!~</strong>" regex
+ | word "<strong>in</strong>" "<strong>{</strong>" list "<strong>}</strong>"
stringcomp ::= word "<strong>==</strong>" word
| word "<strong>-gt</strong>" word | word "<strong>gt</strong>" word
| word "<strong>-ge</strong>" word | word "<strong>ge</strong>" word
-wordlist ::= word
- | wordlist "<strong>,</strong>" word
-
-word ::= word "<strong>.</strong>" word
- | digit
+word ::= digits
| "<strong>'</strong>" string "<strong>'</strong>"
- | "<strong>"</strong>" string "<strong>"</strong>"
+ | '<strong>"</strong>' string '<strong>"</strong>'
+ | word "<strong>.</strong>" word
| variable
- | rebackref
+ | sub
+ | join
| function
+ | "<strong>(</strong>" word "<strong>)</strong>"
-string ::= stringpart
- | string stringpart
+list ::= split
+ | listfunc
+ | "<strong>{</strong>" words "<strong>}</strong>"
+ | "<strong>(</strong>" list "<strong>)</strong>"
-stringpart ::= cstring
+substring ::= cstring
| variable
- | rebackref
-
-cstring ::= ...
-digit ::= [0-9]+
variable ::= "<strong>%{</strong>" varname "<strong>}</strong>"
| "<strong>%{</strong>" funcname "<strong>:</strong>" funcargs "<strong>}</strong>"
+ | "<strong>%{:</strong>" word "<strong>:}</strong>"
+ | "<strong>%{:</strong>" cond "<strong>:}</strong>"
+ | rebackref
+
+sub ::= "<strong>sub</strong>" ["<strong>(</strong>"] regsub "<strong>,</strong>" word ["<strong>)</strong>"]
+
+join ::= "<strong>join</strong>" ["<strong>(</strong>"] list ["<strong>)</strong>"]
+ | "<strong>join</strong>" ["<strong>(</strong>"] list "<strong>,</strong>" word ["<strong>)</strong>"]
+
+split ::= "<strong>split</strong>" ["<strong>(</strong>"] regany "<strong>,</strong>" list ["<strong>)</strong>"]
+ | "<strong>split</strong>" ["<strong>(</strong>"] regany "<strong>,</strong>" word ["<strong>)</strong>"]
+
+function ::= funcname "<strong>(</strong>" words "<strong>)</strong>"
-rebackref ::= "<strong>$</strong>" [0-9]
+listfunc ::= listfuncname "<strong>(</strong>" words "<strong>)</strong>"
-function ::= funcname "<strong>(</strong>" wordlist "<strong>)</strong>"
+words ::= word
+ | word "<strong>,</strong>" list
-listfunction ::= listfuncname "<strong>(</strong>" word "<strong>)</strong>"
+regex ::= "<strong>/</strong>" regpattern "<strong>/</strong>" [regflags]
+ | "<strong>m</strong>" regsep regpattern regsep [regflags]
+
+regsub ::= "<strong>s</strong>" regsep regpattern regsep string regsep [regflags]
+
+regany ::= regex | regsub
+
+regsep ::= "/" | "#" | "$" | "%" | "^" | "|" | "?" | "!" | "'" | '"' | "," | ";" | ":" | "." | "_" | "-"
+
+regflags ::= 1*("i" | "s" | "m" | "g")
+regpattern ::= cstring ; except enclosing <em>regsep</em>
+
+rebackref ::= "<strong>$</strong>" DIGIT
+
+digits ::= 1*(DIGIT)
+cstring ::= 0*(TEXT)
+
+TEXT ::= <any OCTET except CTLs>
+DIGIT ::= <any US-ASCII digit "0".."9">
</pre>
</blockquote>
<p>Some modules register additional variables, see e.g.
<module>mod_ssl</module>.</p>
+ <p>Any variable can be embedded in a <em>string</em>, both in quoted
+ strings from boolean expressions but also in string expressions,
+ resulting in the concatenation of the constant and dynamic parts as
+ expected.</p>
+
+ <p>There exists another form of variables (temporaries) expressed like
+ <code>%{:<em>word</em>:}</code> and which allow embedding of the more
+ powerful <em>word</em> syntax (and constructs) in both type of expressions,
+ without colliding with the constant part of such strings. They are mainly
+ useful in string expressions though, since the <em>word</em> is directly
+ available in boolean expressions already. By using this form of variables,
+ one can evaluate regexes, substitutions, join and/or split strings and
+ lists in the scope of string expressions, hence construct complex strings
+ dynamically.</p>
+
</section>
<section id="binop">
<p>In addition to string-valued functions, there are also
list-valued functions which take one string as argument and return a
- wordlist, i.e. a list of strings. The wordlist can be used with the
+ list, i.e. a list of strings. The list can be used with the
special <code>-in</code> operator. Functions names are not case
sensitive. Modules may register additional functions.</p>
# This delays the evaluation of the condition clause compared to <If>
Header always set CustomHeader my-value "expr=%{REQUEST_URI} =~ m#^/special_path\.php$#"
+# Add a header to forward client's certificate SAN to some backend
+RequestHeader set X-Client-SAN "expr=%{:join PeerExtList('subjectAltName'):}"
+
+# Require that the remote IP be in the client's certificate SAN
+Require expr %{REMOTE_ADDR} -in split s/.*?IP Address:([^,]+)/$1/, PeerExtList('subjectAltName')
+# or alternatively:
+Require expr "IP Address:%{REMOTE_ADDR}" -in split/, /, join PeerExtList('subjectAltName')
+
</highlight>
</section>
<tr><th>Name</th><th>Alternative</th> <th>Description</th></tr>
<tr><td><code>-in</code></td>
<td><code>in</code></td>
- <td>string contained in wordlist</td></tr>
+ <td>string contained in list</td></tr>
<tr><td><code>/regexp/</code></td>
<td><code>m#regexp#</code></td>
<td>Regular expression (the second form allows different