PingGateway 2024.11

Expressions

Use expressions that conform to the Unified Expression Language in JSR-245 to specify configuration parameters as expressions in routes. The result of an expression must match the expected type. For examples of expressions used in routes, refer to Examples.

Use expressions in routes for the following tasks:

Evaluate object properties

The following example returns the URI of the incoming request:

${request.uri}
Call functions decribed in Functions

Functions can be operands for operations and can yield parameters for other function calls.

The following example uses the function find to test whether the request URI is on the /home path:

${find(request.uri.path, '^/home')}
Call Java methods

The following example uses the method Java String.startsWith() to test whether the request starts with /home:

${request.uri.path.startsWith("/home")}
Retrieve Java system properties

The following example yields the home directory of the user executing the PingGateway process:

${system['user.home']}
Retrieve environment variables

The following example yields the home directory of the user executing the PingGateway process:

${env['HOME']}

The following example yields the path value of the keystore.p12 file in the user’s home directory. The result is just a String concatenation, there is no verification that the file actually exists:

${env['HOME']}/keystore.p12`
Perform logical operations such as and, or, and not

The following expression uses the operators and and or to determine where to dispatch a request. The expression is used in Share JWT sessions between multiple instances of PingGateway:

${find(request.uri.path, '/webapp/browsing') and (contains(request.uri.query, 'one') or empty(request.uri.query))}
Perform arbitrarily complex arithmetic, such as addition, substraction, division, and multiplication

The following expression yields the URI port number incremented by four:

${request.uri.port + 4}
Perform relational operations, such as numerical equality and inequality

The following example is for a DispatchHandler condition, where the request is dispatched if it contains a form field with the attribute answer whose value is greater than 42:

"bindings": [
  {
    "condition": "#{request.entity.form['answer'] > 42}",
    "handler": ...
  }
]
Perform conditional operations of this form <condition> ? <if-true> : <if-false>

The following example tests whether the request path starts with /home. If so, the request is directed to home; otherwise, it is directed to not-home:

${request.uri.path.startsWith('/home') ? 'home' : 'not-home' }
Consume evaluated configuration tokens described in JSON Evaluation (runtime expressions only)

The following example returns true if the configuration token my.status.code resolves to 200:

${integer(&{my.status.code|404}) == 200}

Expressions access the environment through the implicit object openig. The object has the following properties:

  • instanceDirectory: Path to the base location for PingGateway files. The default location is:

    • Linux

    • Windows

    $HOME/.openig
    %appdata%\OpenIG\config
  • configDirectory: Path to the PingGateway configuration files. The default location is:

    • Linux

    • Windows

    $HOME/.openig/config
    %appdata%\OpenIG\config
  • temporaryDirectory: Path to the PingGateway temporary files. The default location is:

    • Linux

    • Windows

    $HOME/.openig/tmp
    %appdata%\OpenIG\tmp

To change default values, refer to Change the base location of the PingGateway configuration.

Syntax

Expression syntax must conform to Unified Expression Language described in JSR-245.

Route syntax

Expressions in routes are enclosed in quotation marks, for example: "${request.method}" and "#{expression}".

Immediate and deferred evaluation syntax

Use the ${} syntax for expressions to be evaluated at startup or when a route loads or reloads.

Use the #{} syntax for expressions to be evaluated later, when the evaluation result requires the request or response to be fully loaded.

Operator syntax

AM uses the . and [] operators to access properties:

  • Use . for the following tasks:

    • Access object properties, such as public fields and Java bean properties.

    • Invoke methods on an object.

    • Access map entries when the specified entry name doesn’t contain reserved characters.

  • Use [] to access indexed elements:

    • If the object is a collection, such as an array, set, or list, use [i] to access an element at position i.

    • If the object isn’t a collection, such as an array, set, or list, use ['prop'] to access the property 'prop'. This is equivalent to . notation. The following expressions are equivalent:

      ${request.method}
      ${request['method']}
    • If the object is a map, use ['name'] to access the entry with name 'name'.

To access map entries containing characters that are also expression operators, prevent parsing exceptions by using [] instead of .

For example, to access a map field containing a dash -, such as dash-separated-name, write the expression as:

${data['dash-separated-name']}

instead of:

${data.dash-separated-name}

In the second example, the dash - is interpreted as part of the String instead of as an expression operator.

Array syntax

The index of an element in an array is expressed as a number in brackets. For example, the following expression refers to the first Content-Type header value in a request:

${request.headers['Content-Type'][0]}

Map syntax

The map key is expressed in brackets. For example, the following expression is an example of Map entry access:

system['prop.name']

If a property doesn’t exist, the index reference yields a null (empty) value.

Function syntax

Expressions can call built-in functions described in Functions.

Use the syntax ${function(parameter, …​)}, supplying one or more parameters to the function. For examples, refer to Expressions that use functions.

Functions can be operands for operations and can yield parameters for other function calls.

Method syntax

Use the syntax ${object.method()} to call a method on the object instance.

If the object resolves to a String, then all methods in the java.lang.String class are usable.

For examples, refer to Expressions that use functions.

Escape syntax

The character \ is treated as an escape character when followed by ${ or \#{.

For example, the expression ${true} normally evaluates to true. To include the string ${true} in an expression, write \${true}

When \ is followed by any other character sequence, it isn’t treated as an escape character.

Configuration expressions

PingGateway evaluates configuration expressions at startup and when a route loads or reloads. Configuration expressions are always evaluated immediately and use the ${} syntax.

Configuration expressions can refer to the following information:

  • System heap properties

  • Built-in functions listed in Functions

  • Environment variables, ${env['variable']}

  • System properties, ${system['property']}

  • ExpressionInstant

Because configuration expressions are evaluated before requests are made, they can’t refer to the runtime properties request, response, context, or contexts.

Runtime expressions

PingGateway evaluates runtime expressions for each request and response, as follows:

Immediate evaluation of runtime expressions

If the expression consumes streamed content, for example, the content of a request or response, PingGateway blocks the executing thread until all the content is available.

Runtime expressions whose evaluation is immediate are written with the ${} syntax.

Deferred evaluation of runtime expressions

PingGateway waits until all streamed content is available before it evaluates the expression. Deferred evaluation doesn’t block executing threads.

Runtime expressions whose evaluation is deferred are written with the #{} syntax.

When the streamingEnabled property in admin.json is true, expressions that consume streamed content must be written with # instead of $.

Runtime expressions can refer to the following information:

Embedded expressions

Consider the following points when embedding expressions:

  • System properties, environment variables, or function expressions can be embedded within expressions.

    The following example embeds an environment variable in the argument for a read() function. The value of entity is set to the contents of the file $HOME/.openig/html/defaultResponse.html, where $HOME/.openig is the instance directory:

    "entity": "${read('&{ig.instance.dir}/html/defaultResponse.html')}"
  • Expressions can’t be embedded inside other expressions, as ${expression}.

  • Embedded elements can’t be enclosed in ${}.

Extensions

PingGateway offers a plugin interface for extending expressions. See Key extension points.

L-value expressions

L-value expressions assign a value to the expression scope. For example, "${session.gotoURL}" assignes a value to the session attribute named gotoURL.

PingGateway ignores attempts to write to read-only values.

L-value expressions must be specified using immediate evaluation syntax; use $ instead of #.

Operators

The following operators are provided by Unified Expression Language:

  • Index property value: [], .

  • Change precedence: ()

  • Arithmetic: + (binary), - (binary), *, /, div, %, mod, - (unary)

  • Logical: and, &&, or, ||, not, !

  • Relational: ==, eq, !=, ne, <, lt, >, gt, , le, >=, ge

  • Empty: empty
    Use this prefix operator to determine whether a value is null or empty.

  • Conditional: ?, :

Operators have the following precedence, from highest to lowest, and from left to right:

  • [] .

  • ()

  • - (unary) not ! empty

  • * / div % mod

  • + (binary) - (binary)

  • < > >= lt gt le ge

  • == != eq ne

  • && and

  • || or

  • ? :

Dynamic bindings

Configuration and runtime expressions can use ExpressionInstant.

The current instant is ${now.epochSeconds}.

To add or subtract a period of time to the instant, add one or more of the following time periods to the binding:

  • plusMillis(integer), minusMillis(integer)

  • plusSeconds(integer), minusSeconds(integer)

  • plusMinutes(integer), minusMinutes(integer)

  • plusHours(integer), minusHours(integer)

  • plusDays(integer), minusDays(integer)

The following example binding refers to 30 minutes after the current instant:

${now.plusMinutes(30).epochSeconds}

The following example binding accesses the instant in RFC 1123 date format one day after the current instant:

${now.plusDays(1).rfc1123}

For more examples, refer to the template property of JwtBuilderFilter and the attribute-name property of SetCookieUpdateFilter.

Examples

Immediate evaluation of configuration expressions

The following example yields the value of a secret from a system property.

{
  "passwordSecretId": "${system['keypass']}"
}

The following example yields a file from the home directory of the user running the PingGateway application server.

{
  "url": "file://${env['HOME']}/keystore.p12"
}

The following example of a temporaryStorage object takes the value of the system property storage.ref, which must a be string equivalent to the name of an object defined in the heap:

{
  "temporaryStorage": "${system['storage.ref']}"
}

Deferred evaluation of runtime expressions

The following example is a Route condition, where the Route is accessed if the request contains json with the attribute answer, whose value is 42.

PingGateway defers evaluation of the expression until it receives the entire body of the reqest, transfoms it to JSON view, and then introspects it for the attribute answer.

{
  "condition": "#{request.entity.json['answer'] == 42}",
  "handler": ...
}

The following example expression is for a JwtBuilderFilter that uses the content of the request mapped as a string. PingGateway defers evaluation of the expression until it receives the entire body of the request:

{
  "template": {
    "content": "#{request.entity.string}"
  }
}

Immediate and deferred evaluation of runtime expressions

The following example expressions are for an AssignmentFilter that consumes an ID captured from a response.

PingGateway evaluates the first expression immediately to define the target.

It then defers evaluation of the second expression until it receives the entire body of the response.

"onResponse": [
  {
    "target": "${response.headers['X-IG-FooBar']}",
    "value": "#{toString(response.entity.json['userId'])}"
  }
]

Expressions that use functions

In the following example, "timer" is defined by an expression that recovers the environment variable "ENABLE_TIMER" and transforms it into a boolean. Similarly, "numberOfRequests" is defined by an expression that recovers the system property "requestsPerSecond" and transforms it into an integer:

{
  "name": "throttle-simple-expressions1",
  "timer": "${bool(env['ENABLE_TIMER'])}",
  "baseURI": "http://app.example.com:8081",
  "condition": "${find(request.uri.path, '^/home/throttle-simple-expressions1')}",
  "handler": {
    "type": "Chain",
    "config": {
      "filters": [
        {
          "type": "ThrottlingFilter",
          "name": "ThrottlingFilter-1",
          "config": {
            "requestGroupingPolicy": "",
            "rate": {
              "numberOfRequests": "${integer(system['requestsPerSecond'])}",
              "duration": "10 s"
            }
          }
        }
      ],
      "handler": "ReverseProxyHandler"
    }
  }
}

If "requestsPerSecond"=6 and "ENABLE_TIMER"=true, after the expressions are evaluated PingGateway views the example route as follows:

{
  "name": "throttle-simple-expressions2",
  "timer": true,
  "baseURI": "http://app.example.com:8081",
  "condition": "${find(request.uri.path, '^/home/throttle-simple-expressions2')}",
  "handler": {
    "type": "Chain",
    "config": {
      "filters": [
        {
          "type": "ThrottlingFilter",
          "name": "ThrottlingFilter-1",
          "config": {
            "requestGroupingPolicy": "",
            "rate": {
              "numberOfRequests": 6,
              "duration": "10 s"
            }
          }
        }
      ],
      "handler": "ReverseProxyHandler"
    }
  }
}