[next] [previous] [contents] [full-page]7.1 - Conditional Syntax
7.2 - Conditional Keywords
7.2.1 - Notepad: Keyword
7.2.2 - Rand: Keyword
7.2.3 - Request: Keyword
7.2.4 - Instance: and Robin: Keywords
7.2.5 - Time: Keyword
7.2.6 - Trnlnm: Keyword
7.2.7 - Host Addresses
7.3 - Examples
Request processing (WASD_CONFIG_MAP) and authorization (WASD_CONFIG_AUTH) rules may be conditionally applied depending on request, server or other charactersistics. These include
Conditionals may be nested up to a maximum depth of eight, are not case sensitive and generally match via string comparison, although some tests are performed as boolean operations, by converting the conditional parameter to a number before comparison, and IP address parameters will accept a network mask as well as a string pattern.
The basis of much conditional decision making is string pattern matching. Both wildcard and regular expression based pattern matching is available (6 - String Matching). Wildcard matching in conditional tests is greedy. Regular expression matching, in common with usage throughout WASD, is differentiated from wildcard patterns using a leading "^" character.
Conditional expressions and processing flow structures may be used in the following formats. Conditional and rule text may be indented for clarifying structure.
if (condition) then apply rest of line if (condition) then apply one or more rules up until the corresponding ... endif if (condition) then apply one or more rules else apply one or more other rules up until the corresponding ... endif if (condition) then apply one or more rules elif (condition) apply one or more other rules in a sort or case statement else a possible default rule or rules up until the delimiting endif
Logical operators are also supported, in conjunction with precedence ordering parentheses, allowing moderately complex compound expressions to be applied in conditionals.
There are two more conditional structures that allow previous decisions to be reused. These are unif and the ifif. The first unconditionally includes rules regardless of the current state of execution. The second resumes execution only if the previous if or elif expression was true. The else statement may also be used after an unif to continue only if the previous expression was false. The purpose of these constructs are to allow a single decision statement to include both conditional and unconditional rules.
if (condition) then apply one or more rules unif apply this block of rules unconditionally ifif applied only if the original if expression was evaulated as true unif apply another block of rules unconditionally else and this block of rules only if the original was false endif
CAUTION
Conditional syntax is checked at rule load time (either server startup or reload). Basic errors such as unknown keywords and unbalanced parentheses or structure statements will be detected and reported to the corresponding Admin Menu report and to the server process log. Unless these reports are checked after modifying rule sets syntax errors may result in unexpected mappings or access. Although the server cannot determine the correct intent of an otherwise syntactically correct conditional, if it encounters an unexpected but detectable condition during processing it aborts the request, supplying an appropriate error message.
The following keywords provide a match between the corresponding request or other value and a string immediately following the delimiting colon. White space or other reserved characters may not be included unless preceded by a backslash. The actual value being used in the conditional matching may be observed using the mapping item of the WATCH facility.
|
The request notepad is a string storage area that can be used to store and retrieve ad hoc information during path mapping and subsequent authorization processing. The notepad contents can be changed using the SET notepad=<string> or appended to using SET notepad=+<string> (12.5.5 - SET Rule). These contents then can be subsequently detected using the notepad: conditional keyword (or the obsolescent 'NO' mapping conditional) and used to control subsequent mapping or authorization processing.
NOTE
Notepad information persists across internal redirection processing (12.5.2 - REDIRECT Rule) and so may be used when the regenerated request is mapped and authorized. To prevent such information from unexpectedly interfering with internally redirected requests a notepad="" can be used to empty the storage area.
At the commencement of each pass a new pseudo-random number is generated (and therefore remains constant during that pass). The rand: conditional is intended to allow some sort of distribution to be built into a set of rules, where each pass (request) generates a different one. The random conditional accepts two parameters, a modulas number, which is used to modulas the base number, and a comparison number, which is compared to the modulas result.
Hence the following conditional rules
if (rand:3:0) do this elif (rand:3:1) do this else do this endifwould pseudo-randomly generate base numbers of 0, 1, 2 and perform the appropriate conditional block. Over a sufficient number of usages this should produce a relatively even distribution of numbers. If the modulas is specified as less than two (i.e. no distribution factor at all) it defaults to 2 (i.e. a distribution of 50%). Hence the following example should be the equivalent of a coin toss.
if (rand:) heads else tails endif
Looks through each of the lines of the request header for the specified request field and/or value. This may be used to detect the presence of specific or unknown (to the server) request fields. When detecting a specified just field the name can be provided
if (request:"Keep-Alive:*")matching any value, or specific values can also be matched for
if (request:"User-Agent:*Opera*")
Note that all request fields known to the server have a specific associated conditional keyword (i.e. "user-agent:" for the above example). To determine whether any request fields unknown to the server have been supplied use the request: keyword as in the following example.
if (request:?) map * /cgi-bin/unknown_request_notify.com* endif
Both of these conditionals are designed to allow the redistribution of requests between clustered WASD services. They are WASD-aware and so allow a slightly more tailored distribution than perhaps an IP package round-robin implementation might. Each tests for the current operation of WASD on a particular node (using the DLM) before allowing the selection of that node as a target. This can allow some systems to be shutting down or starting up, or have WASD shutdown for any reason, without requiring any extraordinary procedures to allow for the change in processing environment.
The instance: directive allows testing for a particular cluster member having a WASD instance currently running. This can allow requests to be redirected or reverse-proxied to a particular system with the knowlege that it should be processed (of course there is a small window of uncertainty as events such as system shutdown and startup occur asynchronously). The behaviour of the conditional block is entirely determinate based on which node names have a WASD instance and the order of evaluation. Compare this to a similar construct using the robin: directive, as described below.
This conditional is deployed in two phases. In the first, it contains a comma-separated list of node names (that are expected to have instances of WASD instantiated). In the second, containing a single node name, allowing the selected node to be tested. For example.
if (instance:NODE1,NODE2,NODE3) if (instance:NODE1) redirect /* http://node1.domain.name/*? if (instance:NODE2) redirect /* http://node2.domain.name/*? if (instance:NODE3) redirect /* http://node3.domain.name/*? pass * "500 Some sort of logic error!!" endif pass * "503 No instance currently available!"
If none of the node names specified in the first phase is currently running a WASD instance the rule returns false, otherwise true. If true the above example has conditional block processed with each of the node names successively tested. If NODE1 has a WASD instance executing it returns true and the associated redirect is performed. The same for NODE2 and NODE3. At least one of these would be expected to test true otherwise the outer conditional established during phase one would have been expected to return false.
The robin: conditional allows rules to be applied sequentially against specified members of a cluster that currently have instances of WASD running. This is obviously intended to allow a form of load sharing and/or with redundancy (not balancing, as no evaluation of the selected target's current workload is performed, see below). As with the instance: directive above, there is, of course, a small window of potential uncertainty as events such as system shutdown and startup occur asynchronously and may impact availability between the phase one test and ultimate request distribution.
This conditional is again used in two phases. The first, containing a comma-separated list of node names (that are expected to have instances of WASD instantiated). The second, containing a single node name, allowing the selected node (from phase one) to have a rule applied. For example.
if (robin:VAX1,ALPHA1,ALPHA2,IA64A) if (robin:VAX1) redirect /* http://vax1.domain.name/*? if (robin:ALPHA1) redirect /* http://alpha1.domain.name/*? if (robin:ALPHA2) redirect /* http://alpha2.domain.name/*? if (robin:IA64A) redirect /* http://ia64a.domain.name/*? pass * "500 Some sort of logic error!!" endif pass * "503 No round-robin node currently available!"
In this case round-robining will be made through four node names. Of course these do not have to represent all the systems in the cluster currently available or having WASD instantiated. The first time the 'robin:' rule containing multiple names is called VAX1 will be selected. The second time ALPHA1, the third ALPHA2, and the fourth IA64A. With the fifth call VAX1 is returned to, the sixth ALPHA1, etc. In addition, the selected nodename is verified to have a instance of WASD currently running (using the DLM and WASD's instance awareness). If it does not, round-robining is applied again until one is found (if none is available the phase one conditional returns false). This is most significant as it ensures that the selected node should be able to respond to a redirected or (reverse-)proxied requested. This is the selection set-up phase.
Then there is the selection application phase. Inside the set-up conditional other conditionals apply the selection made in the first phase (through simple nodename string comparison). The rule, in the above example a redirect, is applied if that was the node selected.
During selection set-up unequal weighting can be applied to the round-robin algorithm by including particular node names more than once.
if (robin:VAX1,ALPHA,VAX2,ALPHA)
In the above example, the node ALPHA will be selected twice as often as either of VAX1 and VAX2 (and because of the ordering interleaved with the VAX selections).
The time: conditional allows server behaviour to change according to the time of day, week, or even year. It compares the supplied parameter to the current system time in one of three ways.
if (time:0000-0000) it's midnight elif (time:0001-1159) it's AM elif (time:1200-1200) it's noon else it's PM endif
if (time:6 || time:7) it's the weekend else it's the working week endif
if (time:%%%%-05-*) it's the month of May endif
The trnlnm: conditional dynamically translates a logical name and uses the value. One mandatory and up to two optional parameters may be supplied.
trnlnm:logical-name[;name-table][:string-to-match]
The logical-name must be supplied; without it false is always returned. If just the logical-name is supplied the conditional returns true if the name exists or false if it does not. The default name-table is LNM$FILE_DEV. When the optional name-table is supplied the lookup is confined to that table. If the optional string-to-match is supplied it is matched against the value of the logical and the result returned.
Host names or addresses can be an alpha-numeric string (if DNS lookup is enabled) or dotted-decimal network address, a slash, then a dotted-decimal mask. For example "131.185.250.0/255.255.255.192". This has a 6 bit subnet. It operates by bitwise-ANDing the client host address with the mask, bitwise-ANDing the network address supplied with the mask, then comparing the two results for equality. Using the above example the host 131.185.250.250 would be accepted, but 131.185.250.50 would be rejected. Equivalent notation for this rule would be "131.185.250.0/26".
The following provides a collection of examples of conditional mapping and authorization rules illustrating the use of wildcard matching, network mask matching and the various formats in which the rules may be blocked.
if (query-string:*example*) exec /* /cgi-bin/example/*
if (service:the.host.name:80) pass /web/* /dka0/the_host_name_web/* pass /graphics/* /dka100/graphics/* pass * "404 Resource not found." endif
if (service:the.host.name:80) pass /web/* /dka0/the_host_name_web/* elif (service:next.host.name:80) pass /web/* /dka0/next_host_name_web/* elif (service:another.host.name:80) pass /web/* /dka0/another_host_name_web/* endif pass /graphics/* /dka100/graphics/* pass * "404 Resource not found."
if (server-name:the.host.name) if (scheme:"https") pass /secure/* /dka0/the_host_name_web/secure/* else pass * /dka0/the_host_name_web/secure/only-via-SSL.html endif endif
if (server-name:the.host.name) if (!SSL:) pass * /web/secure/only-via-SSL.html endif pass /secure/* /web/secure/* pass /other/* /web/other/* pass /web/* /web/web/* pass * "404 Resource not found." endif
if (path:/sensitive/* && !(remote-addr:131.185.250.0/24 || SSL:)) pass * 404 "Access denied (SSL only)." endif
[[*]] ["Your VMS password"=VMS] if (!request-scheme:https) * r+w,#0 endif
if (trnlnm:HTTPD_EXAMPLE) pass /* /example/* else pass /* /* endif
$ HTTPD /DO=NOTE=PROBLEM /ALLat the command-line (or via the online equivalent). Note that in this example external clients are provided with the problem advice document while internal clients may still access the back-end for troubleshooting purposes.
if (note:PROBLEM && !remote-addr:131.185.0.0/16) pass /* /problem_with_backend.html else pass /* /backend/* endif
Of course there are a multitude of possibilities based on this idea!
NOTE
The noted data persists across server startups but does not persist across system startups!