Routing Policy Language for Load Balancers
Learn how to write routing policy condition statements that guide a load balancer's routing behavior.
To control how incoming requests to resources like web servers are routed, you must create policies. These policies take a general form of "If this, then forward traffic to a backend set." The backend set must be one you have already created.
Routing policies work in the following ways:
- Each HTTP request is evaluated against the rules.
- The rules are run in the order that is defined in the policy.
- Each rule has at least one condition and a backend set.
- If the HTTP request condition matches a rule, the request is forwarded to the backend set defined for the rule. The other rules in the policy are skipped and the request is not evaluated against them.
Example: One Path Rule
Here's an example of a routing policy rule set that contains only one path-based rule:
{
"name": "BasicPathBasedPolicy",
"conditionLanguageVersion": "V1",
"rules": [
{
"name": "Documents_rule",
"condition" : "any(http.request.url.path eq (i '/documents'))",
"actions": [{
"name": "FORWARD_TO_BACKENDSET",
"backendSetName": "backendSetForDocuments"
}]
}
]
}
This example shows the following elements:
-
The rule set is enclosed in curly brackets
{ }
and contains a name for the rule set, the language version number, and a name for the set of rules. -
The rule set name in the example is
"BasicPathBasedPolicy"
. Rules in the set are contained inside square brackets. -
The sole rule in the set is named
"Documents_rule"
. -
The condition for the rule says that if
any
of the conditions are met, then perform the action in"actions"
. -
The condition compares the incoming HTTP request URL path with
/documents
. The comparison iseq
meaning equals, which could also be written as=
. -
In the condition statement
(i '/documents')
declares that'/documents'
is case-insensitive. -
When the condition is met, the action taken is to forward the request to a specific backend set, in this case "backendSetForDocuments." This backend set must exist for the rule to be valid.
The rule can be paraphrased as "If the requested URL path is an exact match for /documents
then forward the request to the backend set backendSetForDocuments
.
Example: Two Simple Path Rules
Here's an example of a routing policy rule set that contains two simple path-based rules. An incoming query is sent to a different backend set based on the request URL path, and forwarding happens if either the first condition or the second condition is met. Multiple rules are evaluated in their order in the policy. If a query happens to match both of these conditions, the action is performed on the first one matched and the second match is skipped.
{
"name": "PathBasedPolicy",
"conditionLanguageVersion": "V1",
"rules": [
{
"name": "Documents_rule",
"condition" : "any(http.request.url.path eq (i '/documents'))",
"actions": [{
"name": "FORWARD_TO_BACKENDSET",
"backendSetName": "backendSetForDocuments"
}]
},
{
"name": "Videos_rule",
"condition" : "any(http.request.url.path eq (i '/videos'))",
"actions": [{
"name": "FORWARD_TO_BACKENDSET",
"backendSetName": "backendSetForVideos"
}]
}
]
}
Example: One Rule with Two Conditions
The next policy has one rule with two conditions (each condition is separated by a comma). The first condition examines the request headers, and the second condition examines the request's query string:
{
"name": "Example_policy",
"conditionLanguageVersion": "V1",
"rules": [
{
"name": "HR_mobile_user_rule",
"condition" : "all(http.request.headers[(i 'user-agent')] eq (i 'mobile'), http.request.url.query['department'] eq 'HR')",
"actions": [{
"name": "FORWARD_TO_BACKENDSET",
"backendSetName": "backendSetForHRMobileUsers"
}]
}
]
}
The rule now requires that two conditions are both true to forward a request to a backend set, since it begins with the all keyword. The conditions for the rule can be paraphrased as "If the requested user-agent value in the header is set to mobile and the department value in the header is HR, then forward to the specified backend set.
Example: Two Rules
The final example shows two rules. Each rule has a different action, and both rules have two conditions. Importantly, the second rule begins with the keyword any, meaning that only one of the two conditions needs to be true to trigger the action. If more than two conditions specified, if any one of them is true the action is triggered.
{
"name": "Example_policy",
"conditionLanguageVersion": "V1",
"rules": [
{
"name": "HR_mobile_user_rule",
"condition" : "all(http.request.headers[(i 'user-agent')] eq (i 'mobile'), http.request.url.query['department'] eq 'HR')",
"actions": [{
"name": "FORWARD_TO_BACKENDSET",
"backendSetName": "backendSetForHRMobileUsers"
}]
},
{
"name": "Documents_rule",
"condition" : "any(http.request.url.path eq (i '/documents'), http.request.headers[(i 'host')] eq 'doc.myapp.com')",
"actions": [{
"name": "FORWARD_TO_BACKENDSET"
"backendSetName": "backendSetForDocuments"
}]
}
]
}
Rule Conditions
The rule conditions are written in the form of predicates. Multiple predicates can be used in one condition, using combinators. The two combinators: any()
and all()
behave like a logical OR or AND. A combinator can also be negated by putting the keyword not before it. A simple predicate can be expressed as:
left value matcher right value
A condition for a rule that must match if the HTTP request URL path starts with /foo/bar
would be:
http.request.url.path sw '/foo/bar'
More details about the available matchers are in Matchers.
More details about the available variables are in Variables.
Syntax for multiple predicates
not? any|all(<condition>,<condition>,...)
For example:
all(http.request.url.path sw '/foo', 'bar' in (http.request.url.query))
Condition Examples
Here are more examples of how conditions can be used. Here are more details on the exact syntax and functionality.
- To match an HTTP request if its URL path starts with
/category/element
:http.request.url.path sw '/category/element'
- To match an HTTP request if its URL path starts with
/category
or ends with/id
:any(http.request.url.path sw '/category', http.request.url.path ew '/id')
- To match an HTTP request if a
User-Agent
request header is present:(i 'User-Agent') in (http.request.headers)
- To match an HTTP request if the header
User-Agent
has the valueSome User Agent
:http.request.headers[(i 'User-Agent')] eq 'Some User Agent'
- To match an HTTP request if the URL query string has a case-sensitive key
search
, for example as in the URLhttps://www.example.com/category/?search=item+foo%20bar&page=1
:'search' in (http.request.url.query)
- To match an HTTP request if the URL query string has a case-sensitive key
search
(case-sensitively) with a case-insensitive valueitem+foo%20bar
, for example as in the URL:https://www.domain.com/category/?search=item+foo%20bar&page=1
http.request.url.query['search'] = (i 'item foo bar')
Matching for URL query (both keys and values) must be done using URL unescaped versions of their values.
- To case-insensitively match an HTTP request for a cookie named
tastycookie
:(i 'tastycookie') in (http.request.cookies)
- To case-insensitively match an HTTP request for a cookie named
tastycookie
that contains the case-sensitive valuestrawberry
:http.request.cookies[(i 'tastycookie')] = 'strawberry'
Matchers
Multiple matchers are available to use in conditions.
String Matchers
The next table lists the matchers that operate on string values. Some matchers have alternative variants, meaning any of those variants can be used interchangeably for that matcher.
The examples for each matcher all match the http.request.url.path
containing /category/element/id
:
Name | Alternatives | Description | Example |
---|---|---|---|
eq |
=, ==, equal, equals |
Matches if values on the left-hand and right-hand side of the matcher are equal. | http.request.url.path eq "/category/element/id" |
ew |
Matches if the value on the left-hand side ends with the value on the right-hand side. | http.request.url.path ew '/id' |
|
sw |
Matches if the value on the left-hand side starts with the value on the right-hand side. | http.request.url.path sw '/category' |
|
not eq |
!=, not equal, not equals |
Matches if values on the left-hand and right-hand side of the matcher are not equal. | http.request.url.path neq '/some/other/path' |
not ew |
Matches if the value on the left-hand side does not end with the value on the right-hand side. | http.request.url.path not ew '/not_id' |
|
not sw |
Matches if the value on the left-hand side does not start with the value on the right-hand side. | http.request.url.path not sw '/not_category' |
Partial Matchers
Some of the variables used in the rules contain arbitrary key-value maps of data when the rules are run. For example http.request.headers
contains the HTTP request headers. For more details on the available maps, see Variables.
The matchers in
and not in
can be used to check if a map variable contains a specific key. It depends on the variable what the key actually represents.
The syntax for checking if a map variable contains a specific key is:
<key> in (<map variable>)
<key>
must be either a case-sensitive or case-insensitive string.- The right-hand side value must be in parentheses.
For example, this condition matches if the HTTP request has a cookie with the name Foo
:
'Foo' in (http.request.cookies)
Values
The values used in predicates can be either constant values or variables which are evaluated at runtime.
Constants
The rules support string constants written between single-quotes.
Example:
http.request.url.path sw '/foo'
String case-sensitivity
String matching uses case-sensitive comparisons by default.
For example, if the HTTP request URL path for some request is /foo then the following predicate would not match for that request, because case-sensitive string comparison is used:
http.request.url.path eq '/FOO'
Case-insensitive matching is done if at least one of the compared values is a case-insensitive string. The syntax for a case insensitive string is:
(i '<string content>')
For example, these strings are all case-insensitive and are therefore equivalent, when used in predicates:
(i 'foo')
(i 'Foo')
(i 'FOO')
In comparison to the original example - this predicate does match, because it uses case-insensitive comparison:
http.request.url.path eq (i '/FOO')
String Case-Sensitivity
String matching uses case-sensitive comparisons by default.
For example, if the HTTP request URL path for some request is /foo
then the following predicate would not match for that request, because case-sensitive string comparison is used:
http.request.url.path eq '/FOO'
Case-insensitive matching is done if at least one of the compared values is a case-insensitive string. The syntax for a case insensitive string is:
(i '<string content>')
For example, these strings are all case-insensitive and are therefore the same, when used in predicates:
(i 'foo')
(i 'Foo')
(i 'FOO')
In comparison to the original example, this predicate does match, because it uses case-insensitive comparison:
http.request.url.path eq (i '/FOO')
Variables
Variables are used in conditions to match against some particular value of the HTTP request. The actual values for each variable are decided when the rules are run, during each individual HTTP request.
Map Type Variables
Some variables contain arbitrary key-value maps of request data, for example request headers or cookies. For each key, there can be one or more values. For example, there can be several request headers with the same name.
Typically, map variables can be used in rules these ways:
- To check if a map has a specific key.
- To check if a map has a specific key with a specific value.
Checking if a map has a specific key:
Checking if a map variable has a specific key is done with the in
matcher. For more details, refer to Routing Policy Language for Load Balancers.
For example:
'Foo' in (http.request.cookies)
This condition matches if the HTTP request has a cookie with the name Foo
.
Checking if a map has a specific key with a specific value:
Checking if a map has a specific key with a specific value is done using bracket []
notation to get the values at a specific key. The syntax for using bracket notation is:
<variable>[<key>]
The <key>
must be specified as a case-sensitive or case-insensitive string.
The actual check for a specific value is done using the eq
matcher to check if any
of the values at that key are equal to that specific value. The predicate matches if at least one of the values at that key match that specific value.
Examples:
- To match if any value of the header
header-name
equalsheader-value
:http.request.headers[(i 'header-name')] eq 'header-value'
Header
name
is compared case-insensitively, but headervalue
is compared case-sensitively. - To match if any value of the cookie "cookie-name" equals
cookie-value
:http.request.cookies['cookie-name'] eq 'cookie-value'
The not eq
matcher can be used to check that none
of the values at that key are equal to a specific value.
Examples:
- To match if no value of the header
header-name
equalsheader-value
:http.request.headers[(i 'header-name')] not eq 'header-value'
Header name is compared case-insensitively. Header value is compared case-sensitively.
- To match if no value of the cookie "cookie-name" equals
cookie-value
:http.request.cookies['cookie-name'] not eq 'cookie-value'
All Available Variables
The variables available to be used in the conditions are:
Name | Description | Example |
---|---|---|
http.request.headers |
A map containing HTTP request headers. This map has some special behavior - the keys (header names) must be case-insensitive strings. Using case-sensitive strings for |
|
http.request.url.path |
HTTP request URL path. This is the request URL, without protocol, domain, port, and query string. | |
http.request.url.query |
A map containing HTTP request URL query elements. If the request URL doesn't have a query (doesn't have the ? character) or if the query is empty (the ? character is the last one in the URL), this map is empty.The query is parsed to produce the
The first Inside a key-value pair, the first = character present separates the key from the value. If extra = characters are present, they're treated as being part of the value. Keys and values are unescaped according to URL escaping rules. |
URL:https://www.domain.com/path?key=value&key=%61&another%20key=another+value The
In this example, both key and value are matched case-sensitively. So if instead of However, a key-value pair from the URL query string isn't added to the
If the right-hand side of the |
http.request.cookies |
A map containing HTTP request cookies, parsed from the "Cookie" request header as called out in RFC-6265, where the key is a cookie name and the value is the corresponding cookie value. If the "Cookie" request header isn't present in the request - this map is empty. |
Examples
An incoming HTTP/1.1 request looks like this (request line and headers):
GET /category/some_category?action=search&query=search+terms&filters[]=5&features[]=12 HTTP/1.1
Accept-Encoding: gzip, deflate, br
Cookie: cookie_a=1; cookie_b=foo
Host: www.domain.com
User-Agent: Browser Foo/1.0
X-Forwarded-For: 1.2.3.4, 5.6.7.8
X-Forwarded-For: 9.10.11.12
Then the variables available for rules would be populated with data from this request as follows:(the data for structured variables is shown in JSON format)
http.request.url.path: "/category/some_category"
http.request.url.query: {
"action": ["search"],
"query": ["search terms"],
"filters[]": ["5", "12"]
}
http.request.headers: {
"Accept-Encoding": ["gzip, deflate, br"],
"Cookie": ["some_cookie=1; another_cookie=foo"],
"Host": ["www.domain.com"],
"User-Agent": ["Browser Foo/1.0"],
"X-Forwarded-For": ["1.2.3.4, 5.6.7.8", "9.10.11.12"]
}
http.request.cookies: {
"cookie_a": ["1"],
"cookie_b": ["foo"]
}
Here are some examples how we could match this request:
- If we wanted to match requests to domain
www.domain.com
and URL paths that start with/category/
, we would use a condition like this:all(http.request.headers[(i 'Host')] eq 'www.domain.com', http.request.url.path sw '/category')
- To match requests where the URL path is exactly
/category/some_category
or request query elementaction=search
:any(http.request.url.path eq '/category/some_category', http.request.url.query['action'] eq 'search')
- To match requests that have a query string element named
query
with valuesearch terms
(after URL unescaping):http.request.url.query['query'] eq 'search terms'
- To match requests that have cookie
cookie_a
but don't have cookiecookie_c
:all('cookie_a' in (http.request.cookies), 'cookie_c' not in (http.request.cookies))