| .. Licensed to the Apache Software Foundation (ASF) under one | |
| or more contributor license agreements. See the NOTICE file | |
| distributed with this work for additional information | |
| regarding copyright ownership. The ASF licenses this file | |
| to you under the Apache License, Version 2.0 (the | |
| "License"); you may not use this file except in compliance | |
| with the License. You may obtain a copy of the License at | |
| http://www.apache.org/licenses/LICENSE-2.0 | |
| Unless required by applicable law or agreed to in writing, | |
| software distributed under the License is distributed on an | |
| "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY | |
| KIND, either express or implied. See the License for the | |
| specific language governing permissions and limitations | |
| under the License. | |
| Copyright 2020, Verizon Media | |
| .. include:: txnbox_common.defs | |
| .. highlight:: text | |
| .. _expr: | |
| Feature Expressions | |
| ******************* | |
| A feature expression specifies how to extract a feature. At run time, if the feature is needed, the | |
| expression is applied and the result is the feature. The feature can be any of the types | |
| =================== ========================================================================== | |
| Type Description | |
| =================== ========================================================================== | |
| String Text. | |
| NULL The null value, no value. | |
| Integer A signed integral value. | |
| Boolean A value that is ``true`` or ``false``. | |
| IP Address An IP address. | |
| Duration A time span. | |
| Tuple A list of features. | |
| =================== ========================================================================== | |
| Expressions vary from simple to very complex, but ultimately produce a feature of one of these types. | |
| Basic Expressions | |
| ================= | |
| Basic feature expressions are very similar to `Python format strings | |
| <https://docs.python.org/3.4/library/string.html#format-string-syntax>`__ which contains a mixture | |
| of literal strings and extractors. Extractors are referenced by what Python calls a "replacement | |
| field" which are set off by braces. The underlying format structure is :: | |
| **{** [ *name* ] [ **:** [ *format* ] [ **:** *extension* ] ] **}** | |
| :code:`name` specifies the extractor, :code:`format` makes it possible to control the output of the | |
| extractor (such as width), and :code:`extension` is used to pass extra data to the extractor. | |
| In general the result of a quoted string is a string feature. All extractors can generate text output | |
| and so can be used in a feature expression.. As a special case, if the format expression consists of | |
| a single extractor and no literal text then the result type is the result type of the extractor. | |
| A feature expression can be preceded by the YAML type tag ``!literal`` in which case the string is | |
| treated as a literal string, no extractors are invoked. Doubling braces will escape the braces and | |
| yield a single one, the same way as for Python. | |
| And finally, as unquoted string is parsed differently. It is checked if it can be parsed as one of | |
| a particular set of literals and if so is treated as such. | |
| * The string "NULL", which yields the no value feature. | |
| * integer | |
| * boolean | |
| * IP address | |
| If none of these, it is treated as an extractor, e.g. as if it had been enclosed by ``"{}"``. This | |
| is by far the most common case and so it is useful and clearer to skip the quotes and braces. | |
| A feature expression can include :term:`modifier`\s. These modify or transform an extracted feature. | |
| The base expression is used to extract a feature, then the modifiers are applied to the feature | |
| sequentially to yield the feature. |TxB| uses a `data flow | |
| <https://en.wikipedia.org/wiki/Dataflow_programming>`__ style where most feature manipulations are | |
| expected to be done by modifiers on expressions. | |
| Some examples - :: | |
| "Delain concert" | |
| The literal string "Delain concert". :: | |
| "Forwarded for {inbound-addr-remote} to {ua-req-host}" | |
| This extracts a string that could be "Forwarded for 192.23.34.99 to apache.org". It invokes the | |
| extractors ``inbound-addr-remote`` and ``ua-req-host`` which extract the client IP address and | |
| the host for the client request respectively. :: | |
| !literal "Forwarded for {inbound-addr-remote} to {ua-req-host}" | |
| The literal string "Forwarded for {inbound-addr-remote} to {ua-req-host}". :: | |
| "Forwarded for {{inbound-addr-remote}} to {{ua-req-host}}" | |
| The literal string "Forwarded for {inbound-addr-remote} to {ua-req-host}". :: | |
| 56 | |
| The integer constant 56. :: | |
| NULL | |
| The null feature. :: | |
| "NULL" | |
| The literal string "NULL". :: | |
| inbound-addr-remote | |
| The extractor ``inbound-addr-remote`` which extracts an IP address feature with the value of the | |
| client source address. This extracts a feature of type ``IP_Address``. This identical to :: | |
| "{inbound-addr-remote}" | |
| Formatting | |
| ========== | |
| The second part of an extractor supports controlling the format of the output. This is not generally | |
| required but in some cases it is quite useful. A good example is the extractor | |
| :txb:ex:`is-internal`. This returns a true or false value, which is in the C style mapped to 1 | |
| and 0. However, it can be changed to "true" and "false" by specifying the output as a string. :: | |
| proxy-req-field<Carp-Internal>: is-internal:s | |
| Formatting is most commonly useful when setting values, such as field values. The extracted strings | |
| can be justified, limited in width, and in particular IP addresses can be formatted in a variety of | |
| ways. | |
| Scoped Extractors | |
| ================= | |
| There are sets of extractors that extract data from a :term:`scope`. A scope becomes :term:`active` | |
| at a specific point in a configuration and remains active for all nested configuration unless | |
| overridden by a scope of the same type. Multiple scopes of the same type nest in the standard scope | |
| semantics, where when a scope becomes inactive the most recently active scope becomes active again. | |
| Any scope extractors extract data from the most recent / innermost scope. | |
| Regular Expression Scope | |
| ------------------------ | |
| If a regular expression comparison is successful matched, or explicitly applied, this creates a | |
| scope where the capture groups are accessible via index extractors.These are numbered in the | |
| standard way, with ``0`` meaning the entire matched string, ``1`` the first capture group, ``2`` the | |
| second capture group, and so on. It is an error to use an index that is larger than the available | |
| capture groups, or outside an active scope. For example if a header named "mail-check" should be set | |
| if the host contains the domain "mail", it could be done as | |
| .. code-block:: yaml | |
| with: ua-req-host | |
| select: | |
| - rxp: "^(?:(.*?)[.])?mail(?:[.](.*?))?$" | |
| do: | |
| - proxy-req-field<mail-check>: "You've got mail from {2}!" | |
| This is a case where a feature string must be used instead of an unquoted extractor. That is :: | |
| proxy-req-field<mail-check>: 2 | |
| sets the field to the integer value 2, which fails. Similarly :: | |
| proxy-req-field<mail-check>: "2" | |
| sets the field to the string "2". The correct usage is :: | |
| proxy-req-field<mail-check>: "{2}" | |
| sets the field to the second capture group. |