|1Conditional Configuration|| |^ Request processing (WASD_CONFIG_MAP) and authorization (WASD_CONFIG_AUTH) rules may be conditionally applied depending on request, server or other charactersistics. These include |simple#| |item| server host name, port |item| client IP address and host name |item| browser-accepted content-types, character sets, languages, encodings |item| browser identification string |item| scheme ("http:" or "https:", i.e. is it a secure request?) |item| HTTP method (GET, POST, etc.) |item| request path, query string, cookie data, refering page |item| virtual host:port specified in request header |item| system information (hardware, Alpha/IA64/X86, node name, VMS version, etc.) |item| local time |item| random number generation |!simple| |2Service Conditionals| |^ As described in |link|[[virtual-server]]|| a [[|/host||:|/port||]] rule applies subsequent configuration depending on whether the request service matches the specified service. This makes it a fundamental element of conditional configuration. |^ Note that service conditionals impose a boundary on the scope of |/if..endif|| constructs. That is, an |/if..endif|| may not span a virtual service conditional. A conditional flow syntax error is reported if an |/if..endif|| construct is not properly closed before encountering a subsequent [[|/host||:|/port||]] rule. |2If..endif Conditionals| |^ These 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. |0String Matching|| |^ The basis of much conditional decision making is string pattern matching. Both wildcard and regular expression based pattern matching is available (|link|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. |0Conditional Syntax|| |^ Conditional expressions and processing flow structures may be used in the following formats. Conditional and rule text may be indented for clarifying structure. |code| |*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|| |!code| |^ Logical operators are also supported, in conjunction with precedence ordering parentheses, allowing moderately complex compound expressions to be applied in conditionals. |table| |~ |:= ! |. logical negation |~ |:= &&\ \ \ |. logical AND |~ |:= \|\| |. logical OR |!table| |^ 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. |code| |*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|| |!code| |note| |0CAUTIONS| 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. |^ Flow control errors (e.g. an |/if|| not closed by a subsequent |/endif||) abort all rule processing and provide a fatal error report to the client. |!note| |2Conditional Keywords| |^ 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. |0Conditional Keywords| |table| |~_ |: Keyword|: Description |~ |~#* |. accept: |. Browser-accepted content types as listed in the "Accept:" request header field. Same string as provided in CGI variable HTTP_ACCEPT. |~ |. accept-charset: |. Browser-accepted character sets as listed in the "Accept-Charset:" request header field. CGI variable HTTP_ACCEPT_CHARSET. |~ |. accept-encoding: |. Browser-accepted content encoding as listed in the "Accept-Encoding:" request header field. CGI variable HTTP_ACCEPT_ENCODING. |~ |. accept-language: |. Browser language preferences as listed in the "Accept-Language:" request header field. CGI variable HTTP_ACCEPT_LANGUAGE. |~ |. authorization: |. The raw authorization string from the request header, if any supplied. This could be simply used to test whether it has been supplied or not. |~ |. callout: |. Simple boolean value. If a script callout is in progress (see "Scripting Overview, CGI Callouts".) it is true, otherwise false. |~ |. client_connect_gt: |. An integer representing the current network connections (those currently being processed plus those currently being "kept alive") for the particular client represented by the current request. If greater than this value returns true, otherwise false. See |link|Client Concurrency||. |~ |. cluster_member: |. If the supplied node name is (perhaps currently) a member of the cluster (if any) the server may be executing on. |~ |. command_line: |. The command line qualifiers and parameters used when the server image was activated. |~ |. cookie: |. Raw cookie data as the text string provided in "Cookie:" request header field. CGI variable HTTP_COOKIE. |~ |. decnet: |. Whether DECnet is active on the system and which version is available. This value will be 0 if not active, 4 if PhaseIV or 5 is PhaseV. |~ |. dict: |. Matches the specified dictionary entry. See |link|WATCH Dictionary||. |~ |. directory: |. Tests whether the specified directory exists or not. Parameter can be a URI available for mapping by the server or a VMS file-system specification. If no parameter is supplied the request path is mapped to a file-system specification. As this conditional accesses the file-system it can be |/relatively expensive in terms of server latency||. |~ |. document_root: |. The DOCUMENT_ROOT CGI variable SET using the |/map=root=|| mapping rule. |~ |. file: |. Tests whether the specified file exists or not. Parameter can be a URI available for mapping by the server or a VMS file-system specification. If no parameter is supplied the request path is mapped to a file-system specification. The specification can be a directory. As this conditional accesses the file-system it can be |/relatively expensive in terms of server latency||. |~ |. forwarded: |. Proxy/gateway host(s) request forwarded by, as specified in request header field "Forwarded:". CGI variable HTTP_FORWARDED. |~ |. host: |. The host (and optionally port) specified in request header "Host:" field. This is used by all modern browsers to provide virtual host information to the server. CGI variable HTTP_HOST. |~ |. http2: |. Is true if the request is being transported using HTTP/2 |~ |. instance: |. Used to check whether a particular, clustered instance of WASD is available. See |link|Instance: and Robin: Keywords||. |~ |. jpi_username: |. The account username the server is executing as. |~ |. mapped_path: |. The path resulting from mapping (phase 2 if script path involved) from which the path-translated is derived. |~ |. multihome: |. Somewhat specialised conditional that becomes non-null when a client used a different IP address to connect to the service than the is bound to. Is set to the IP address the client used and may be matched using wildcard matching or as a network mask. |~ |. note: |. Ad hoc information (string) provided by the server administrator using the /DO=NOTE= facility (and online equivalent) that can be used to quickly and easily modify rule processing on a per-system or per-cluster basis. |~ |. notepad: |. Information (strings) stored using the SET |/notepad=|| mapping rule. See |link|Notepad: Keyword||. |~ |. ods: |. Specified as 2 or 5 (Extended File System), or as SRI file name encoding (MultiNet NFS and others) PWK encoding (PATHWORKS 4/5), ADS encoding (Advanced Server / PATHWORKS 6), SMB encoding (Samba - same as ADS). |~ |. pass: |. A numeric value, 1 or 2, representing the first or second pass (if a script component was parsed) through the path mapping rules. Will be zero at other times. When the server is |/reverse-mapping|| a file specification will be -1. |~ |. path-info: |. Path specified in the request line. CGI variable PATH_INFO. |~ |. path-translated: |. VMS translation of path-info. Available after rule mapping (i.e. during authorization rule processing). |~ |. proctor: |. Simple boolean value. If a proctored script this is true (see |link%|../scripting/##Script Proctor++in++WASD Scripting||). |~ |. query-string: |. Query string specified in request line. Same information as provided in CGI variable QUERY_STRING. |~ |. rand: |. Value from a random number generator. See |link|Rand: Keyword||. |~ |. redirected: |. If a request has been internally redirected (|link|REDIRECT Rule||) this conditional will be non-zero. Can be used as a boolean or with a digit specified. |~ |. referer: |. URL of refering page as provided in "Referer:" request header field. CGI variable HTTP_REFERER. |~ |. regex: |. Simple boolean value. If configuration directive [RegEx] is enabled (and hence regular expression string matching, |link|String Matching||) this will be true. |~ |. remote-addr: |. Client IP address. Same as provided as CGI variable REMOTE_ADDR. As with all IP addresses used for conditional testing this may be wildcard string match or network mask expressed as |/address||/|/mask-length|| (see |link|Host Addresses||). A domain (host) name preceded by a question point may be specified (e.g. "?the.host.name"). The corresponding IP address is then looked up and compared to the client. This allows ad hoc host name based rules and is distinct from use of |/remote-host||. Note that DNS lookup can introduce some latency to rule (and request) processing. |~ |. remote-host: |. Client host name if name resolution enabled, otherwise the IP address (same as |/remote-addr||). CGI variable REMOTE_HOST. |^+ |*Note:| if name resolution is enabled and host unresolved then conditional |=!remote-host:| will return true indicating no host name. Most legitimate clients have an associated host name. Many illegitimate probers have none. |~ |. request: |. Detect the presence of specific or unknown request fields. See |link|Request: Keyword||. |~ |. request-method: |. HTTP method ("GET", "POST", etc.) specified in the request line. CGI variable REQUEST_METHOD. |~ |. request-protocol: |. Detect the HTTP protocol in use for the request, as "2", "1.1", "1.0" or "0.9". Note that the |/server-protocol|| conditional will indicate 1.1 when the |/request-protocol|| indicates 2. The server and its applications (scripts) still treat it semantically as HTTP/1.1. |~ |. request-scheme: |. Request protocol as "http:" or "https:". CGI variable REQUEST_SCHEME. |~ |. request-uri: |. The unescaped request path plus any query-string. CGI variable REQUEST_URI. |~ |. restart: |. A numeric value, zero to maximum, representing the number of times path mapping has been SET |/map=restart||. Can be used as a boolean or with a digit specified. |~ |. robin: |. Used to check whether a particular, clustered instance of WASD is available and distribute requests to it using a round-robin algorithm. See |link|Instance: and Robin: Keywords||. |~ |. script-name: |. After the first pass of rule mapping (script component resolution), or during authorization processing, any script component of the request URI. |~ |. server-addr: |. The service IP address. CGI variable SERVER_ADDR. This may be wildcard string match or network mask expressed as |/address||/|/mask-length||. |~ |. server_connect_gt: |. An integer representing the current server network connections (those currently being processed plus those currently being "kept alive"). If greater than this value returns true, otherwise false. |~ |. server_process_gt: |. An integer representing the current server requests in-progress. If greater than this value returns true, otherwise false. |~ |. server-name: |. The (possibly virtual) server name. This may or may not exactly match any string provided via the |/host|| keyword. CGI variable SERVER_NAME. |~ |. server-port: |. The (possibly virtual) server port number. CGI variable SERVER_PORT. |~ |. server-protocol: |. "1.1", "1.0", "0.9" representing the HTTP protocol used by the request. |~ |. server-software: |. The server identification string, including the version. For example "HTTPd-WASD/8.0.0 OpenVMS/AXP SSL". CGI variable SERVER_SOFTWARE. |~ |. service: |. This is the composite server name plus port as |/server-name||:|/port||. To match an unknown service use "?". |~ |. ssl: |. Simple boolean value. If request is via Secure Sockets Layer then this will be true. |~ |. syi_arch_name: |. System information; CPU architecture of the server system, "Alpha", "Itanium" or "x86-64". |~ |. syi_hw_name: |. System information; hardware identification string, for example "AlphaStation 400 4/233". |~ |. syi_nodename: |. System information; the node name, for example "KLAATU". |~ |. syi_version: |. System information; VMS version string, for example "V7.3". |~ |. tcpip: |. A string derived from the UCX$IPC_SHR shareable image. It looks something like this "Compaq TCPIP$IPC_SHR V5.1-15 (11-JAN-2001 02:28:33.95)" and comprises the agent (Compaq, MultiNet, TCPware, unknown), the name of the image, the version and finally the link date. |~ |. time: |. Compare to current system time. See |link|Time: Keyword||. |~ |. trnlnm: |. Translate a logical name. See |link|Trnlnm: Keyword||. |~ |. upstream-addr: |. Client proxy/accelerator IP address, when "SET CLIENT=keyword" has been applied to enable transparent up-stream proxy. Same as provided as CGI variable UPSTREAM_ADDR. As with all IP addresses used for conditional testing this may be wildcard string match or network mask expressed as |/address||/|/mask-length|| (see |link|Host Addresses||). |~ |. user-agent: |. Browser identification string as provided in "User-Agent:" request header field. CGI variable HTTP_USER_AGENT. |~ |. webdav: |. Simple boolean value. If the request has been identified as WebDAV then this is true. Takes an optional parameter: |table| |~ |. webdav:all |. True if path has been |/SET webdav=all| |~ |. webdav:auth |. True if path has been |/SET webdav=auth| |~ |. webdav:MSagent |. True if a Microsoft WebDAV agent has been detected. |!table| |~ |. websocket: |. Simple boolean value. If a WebSocket protocol upgrade request will be true. |~ |. x-forwarded-for: |. Proxied client name or address as provided in "X-Forwarded-For:" request header field. CGI variable HTTP_X_FORWARDED_FOR. |!table| |3Notepad: Keyword| |^ 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=|| or appended to using SET |/notepad=+|| (|link|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. |^ Notepad information persists across internal redirection processing (|link|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. |^ The |/dictionary|| facility provides similar and arguably superior functionailtiy. See |link|WATCH Dictionary||. In fact |/notepad|| is now implemented as a dictionary entry. |3Rand: Keyword| |^ 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 | |code| if (rand:3:0) |/do this|| elif (rand:3:1) |/do this|| else |/do this|| endif |!code| | would 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. | |code| if (rand:) |/heads|| else |/tails|| endif |!code| |3Request: Keyword| |^ 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 | |code| if (request:"Keep-Alive:*") |!code| | matching any value, or specific values can also be matched for | |code| if (request:"User-Agent:*Opera*") |!code| |^ 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. | |code| if (request:?) map * /cgi-bin/unknown_request_notify.com* endif |!code| |3Instance: and Robin: Keywords| |^ 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. |0Instance:| |^ 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. |code| 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!" |!code| |^ 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. |0Robin:| |^ 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. |code| if (robin:X861,ALPHA1,ALPHA2,IA64A) if (robin:X861) redirect /* http://x861.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!" |!code| |^ 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 X861 will be selected. The second time ALPHA1, the third ALPHA2, and the fourth IA64A. With the fifth call X861 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. |code| if (robin:X861,ALPHA,X862,ALPHA) |!code| |^ In the above example, the node ALPHA will be selected twice as often as either of X861 and X862 (and because of the ordering interleaved with the X86 selections). |3Time: Keyword| |^ 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. |number| |item| The supplied parameter is in the form "1200-1759", which should be read as "twelve noon to five fifty-nine PM" (i.e. as a time range in minutes, generalized as |/hhmm-hhmm||), where the first is the start time and the second the end time. If the current time is within that range (inclusive) the conditional returns true, otherwise false. If the range doesn't look correct false is always returned. |code| 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 |!code| |item| If the supplied parameter is a single digit it is compared to the VMS day of the week (1-Monday, 2-Tuesday |...| 7-Sunday). |code| if (time:6 \|\| time:7) |/it's the weekend|| else |/it's the working week|| endif |!code| |item| If the supplied string is not in either of the formats described above it is treated as a string match with a VMS comparision time (i.e. |/yyyy-mm-dd hh-mm-ss.hh||). |code| if (time:%%%%-05-*) |/it's the month of May|| endif |!code| |!number| |3Trnlnm: Keyword| |^ The |/trnlnm:|| conditional dynamically translates a logical name and uses the value. One mandatory and up to two optional parameters may be supplied. |code| trnlnm:logical-name[;name-table][:string-to-match] |!code| |^ 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. |3Host Addresses| |^ 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". |2Examples| |^ 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. |number| |item| This first example shows an EXEC mapping rule being applied to a path if the request query string contains the string "example". |code| if (query-string:*example*) exec /* /cgi-bin/example/* |!code| |item| In this example a block of mapping statements is processed if the virtual service of the request matches that in the conditional, otherwise the block is skipped. Note the indentation to help clarify the structure. |code| if (service:the.host.name:80) pass /web/* /dka0/the_host_name_web/* pass /graphics/* /dka100/graphics/* pass * "404 Resource not found." endif |!code| |item| This example a series of tests allow a form of case processing where the first to match will be processed and terminate the matching process. In this case if a match does not occur rule processing continues after the |/endif||. |code| 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." |!code| |item| In this (somewhat contrived) example a nested test is used to check (virtual) server name and that the request is being handled via Secure Sockets Layer (SSL) for security. If it is not an informative message is supplied. The |/else|| and the quotes are not really required but included here for illustration. |code| 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 |!code| |item| This would be another way to accomplish a similar objective to example 4. This uses a |/negation|| operator to exclude access to successive mappings if not requesting via SSL. |code| 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 |!code| |item| This example shows the use of a compound conditional using the AND and OR operators. It also illustrates the use of a network mask. It will exclude all access to the specified path unless the request is originating from within a specified network (perhaps an intranet) or via SSL. |code| if (path:/sensitive/* && !(remote-addr:131.185.250.0/24 \|\| SSL:)) pass * 404 "Access denied (SSL only)." endif |!code| |item| This example illustrates restricting authentication to SSL. |code| [[*]] ["Your VMS password"=VMS] if (!request-scheme:https) * r+w,#0 endif |!code| |item| Logical name translation may be used to dynamically alter the flow of rule interpretation. |code| if (trnlnm:HTTPD_EXAMPLE) pass /* /example/* else pass /* /* endif |!code| |item| Using a site administrator's /DO=NOTE= entry to modify rule processing. In this example the contingency of a broken back-end processor has been prepared for and a document advising clients of the temporary problem is redirected to once the administrator enters |code| $ HTTPD /DO=NOTE=PROBLEM /ALL |!code| at 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. |code| if (note:PROBLEM && !remote-addr:131.185.0.0/16) pass /* /problem_with_backend.html else pass /* /backend/* endif |!code| |^ Of course there are a multitude of possibilities based on this idea! |!number| |note><| The noted data persists across server startups but does not persist across system startups! |!note| |2Dictionary| |^ The per-request dictionary stores key-value string pairs related to request processing. Some entries are generated and used internally by the server and others may be inserted, value changed, removed and tested by the server admin for conditional processing purposes. |^ The dictionary was initially introduced as an abstraction layer between the significantly different HTTP/2 and HTTP/1.|/n|| header semantics and server internal processing. Its utility was then extended into configuration. It is implemented as a standard hash table with collision lists. The small cost in terms of processing is completely offset by its effectiveness. |3Configuration Entries| |^ Dictionary entries may be configured using the SET dict=|/key||=|/value|| mapping rule or the DICT |/key||=|/value|| meta keyword. These are known as |/configuration entries||. Keys must begin with an alpha-numeric character but otherwise keys and values may contain any printable character, with some needing to be escaped in the text of configuration files. These are some examples of each. |code| set /example/path* dict=example_key=example\ value set /example/path* dict=example_key="example value" set /example/path* dict=example_key="example \"value\"" dict example_key=example\ value dict example_key="example value" dict example_key="example \"value\"" |!code| |^ If an existing key is (re-)inserted it overwrites the old value. |^ An entry can have an empty value. |code| set /example/path* dict=example_key= dict example_key= |!code| |^ An entry may be removed from the dictionary by prefixing the key name with an exclamation point. |code| set /example/path* dict=!example_key dict !example_key |!code| |^ All configuration entries may be removed by using the exclamation point with an empty key. |code| set /example/path* dict=! dict ! |!code| |note| Configuration entries persist across internal redirection processing (|link|REDIRECT Rule||) and so may be used as flags or otherwise contain useful information when the regenerated request is mapped and authorized. To prevent such information from unexpectedly interfering with internally redirected requests selected or all entries can be removed in the redirected request using the above values. |!note| |3Other Entries| |^ As mentioned, the server generates and uses dictionary entries during request processing. There are multiple types of entry, generally insulated from each other for good reason. These entries are also available for conditional testing. |0Dictionary Entries| |table| |~_ |: Character|: Type|: Description |~ |~ |. ~ |. configuration |. admin managed entry |~ |. $ |. internal |. server processing |~ |. > |. request |. request header field |~ |. < |. response |. response header field |!table| |^ The "if (dict:|/expression||)" contruct first checks for a configuration entry, then for an request header field entry, then finally for an internal entry (response entries are only available for testing after response processing begins and so not in the search list). It is also possible to test for a key of a specific type by prefixing the key name with the type character. This example shows a request header field being conditionally processed. |code| if (dict:>X-example=hello) |!code| |^ It is also possible to set an entry of a specific type by prefixing the key with the type character. For example the following will set a response header field that will be included in the header when returned to the client. |code| set /example/path* dict=user-agent' |!code| A similar rule can be seen applied in the WATCH report example below. |3WATCH Dictionary| |^ The content of a request's dictionary at significant stages of request processing can be viewed using the [x]Internal item of a WATCH report. See |link%|../features/##WATCH Facility| of |link%|../features/##|WASD Features and Facilities||. |^ A request dictionary WATCH point is similar to the following (end of request processing) example. Note that all of the entry types described above are present in the example, including two configured entries. Note also that two of the internal entries contain embedded line-breaks and empty lines. This is an HTTP/2 request and the expanded (HTTP/1.|/n|| style) |/request_header|| and |/response_header|| entries are due to WATCH items Request [x]Header and Response [x]Header also being checked. They were not required for request processing. |code| \|Time_______\|Module__\|Line\|Item\|Category__\|Event...\| |/8< snip 8<|| \|21:11:00.12 DICT 0836 0001 INTERNAL DICTIONARY size:32 count:29 bytes:4193\| ENTRY 001 [005] $ {14}request_method={3}GET ENTRY 002 [009] $ {12}request_path={15}/httpd/-/admin/ ENTRY 003 [014] > {6}accept={63}text/html,application/xhtml+xml,application/xml;q=0.9,*/*;q=0.8 ENTRY 004 [018] > {15}accept-encoding={13}gzip, deflate ENTRY 005 [001] > {10}user-agent={116}Mozilla/5.0 (Macintosh; Intel Mac OS X 10_11_3) AppleWebKit/601.4.4 (KHTML, like Gecko) Version/9.0.3 Safari/601.4.4 ENTRY 006 [007] > {15}accept-language={5}en-us ENTRY 007 [031] > {13}authorization={30}Basic ************************* ENTRY 008 [004] > {3}dnt={1}1 ENTRY 009 [012] $ {12}request_line={28}GET /httpd/-/admin/ HTTP/1.1 ENTRY 010 [024] > {4}host={18}klaatu.private:443 ENTRY 011 [011] $ {10}http2_ping={6}44.919 ENTRY 012 [013] $ {14}request_header={372}GET /httpd/-/admin/ HTTP/1.1 accept: text/html,application/xhtml+xml,application/xml;q=0.9,*/*;q=0.8 accept-encoding: gzip, deflate user-agent: Mozilla/5.0 (Macintosh; Intel Mac OS X 10_11_3) AppleWebKit/601.4.4 (KHTML, like Gecko) Version/9.0.3 Safari/601.4.4 accept-language: en-us authorization: Basic ************************* dnt: 1 host: klaatu.private:443 ENTRY 013 .012. $ {9}path_info={15}/httpd/-/admin/ ENTRY 014 [000] $ {12}query_string={0} ENTRY 015 .004. $ {11}request_uri={15}/httpd/-/admin/ ENTRY 016 [025] ~ {7}this_is={7}a test! ENTRY 017 [028] < {12}x-user-agent={116}Mozilla/5.0 (Macintosh; Intel Mac OS X 10_11_3) AppleWebKit/601.4.4 (KHTML, like Gecko) Version/9.0.3 Safari/601.4.4 ENTRY 018 .018. $ {15}response_status={3}200 ENTRY 019 [026] $ {15}response_reason={2}OK ENTRY 020 .011. < {6}server={33}HTTPd-WASD/11.0.0 OpenVMS/AXP SSL ENTRY 021 [002] < {4}date={29}Tue, 02 Feb 2016 10:40:59 GMT ENTRY 022 .005. < {13}accept-ranges={5}bytes ENTRY 023 [008] < {15}accept-encoding={13}gzip, deflate ENTRY 024 .004. < {7}expires={29}Fri, 13 Jan 1978 14:00:00 GMT ENTRY 025 [030] < {13}cache-control={18}no-cache, no-store ENTRY 026 .028. < {6}pragma={8}no-cache ENTRY 027 .030. < {12}content-type={29}text/html; charset=ISO-8859-1 ENTRY 028 [006] < {14}content-length={5}15741 ENTRY 029 [019] $ {15}response_header={446}HTTP/1.1 200 OK x-user-agent: Mozilla/5.0 (Macintosh; Intel Mac OS X 10_11_3) AppleWebKit/601.4.4 (KHTML, like Gecko) Version/9.0.3 Safari/601.4.4 server: HTTPd-WASD/11.0.0 OpenVMS/AXP SSL date: Tue, 02 Feb 2016 10:40:59 GMT accept-ranges: bytes accept-encoding: gzip, deflate expires: Fri, 13 Jan 1978 14:00:00 GMT cache-control: no-cache, no-store pragma: no-cache content-type: text/html; charset=ISO-8859-1 content-length: 15741 |/8< snip 8<|| |!code| |^ The first three digit number is simply the entry count in order of insertion. The second, either square bracketed or period delimited, is the hash table entry. The square brackets indicate the head of the hash table, the periods down the collision list. The single punctuation character is use to indicate and differentiate the entry type. Then are the key and equate-separated value. The brace enclosed numbers are the length of the key and value respectively.