Session Groups and Classification

How to classify clients into session groups and use them in routing

ESB3024 Router provides a flexible classification engine, allowing the assignment of clients into session groups that can then be used to base routing decisions on.

Session Classification

In order to perform routing it is necessary to classify incoming sessions according to the relevant parameters. This is done through session groups and their associated classifiers.

There are different ways of classifying a request:

  • Anonymous IP: Classifies clients using an anonymous IP database. See Geographic Databases for information about the database.
  • ASN IDs: Checks to see if a client’s IP belongs to any of the specified ASN IDs. See Geographic Databases for information about the ASN database.
  • Content URL path: Matches the given pattern against the path part of the URL requested by the client. The match can be either a case-insensitive wildcard match or a regular expression match.
  • Content URL query parameters: Matches the given pattern against the query parameters of the URL requested by the client. The query parameters are passed as a single string. The match can be either a case-insensitive wildcard match or a regular expression match.
  • GeoIP: Based on the geographic location of the client, supporting wildcard matching. See Route on GeoIP/ASN for more details. The possible values to match with are any combinations of:
    • Continent
    • Country
    • Region
    • Cities
    • ASN
  • Host name: Matches the given pattern against the name of the host that the request was sent to. The match can be either a case-insensitive wildcard match or a regular expression match.
  • IP ranges: Classifies a client based on whether its IP address belongs to any of the listed IP ranges or not.
  • Random: Randomly classifies clients according to a given probability. The classifier is deterministic, meaning that a session will always get the same classification, even if evaluated multiple times.
  • Regular expression matcher: Matches the given pattern against a configurable source. The match is case-insensitive and supports regular expressions. The following sources are available:
    • content_url_path: The path part of the URL requested by the client.
    • content_url_query_params: The query parameters of the URL requested by the client. The query parameters are passed as a single string.
    • hostname: The name of the host that the request was sent to.
    • user_agent: The user agent string in the HTTP request from the client.
  • Request Header: Classifies clients based on the value of a specified HTTP header in the request from the client.
  • String matcher: Matches the given pattern against a configurable source. The match is case-insensitive and supports wildcards (’*’). The following sources are available:
    • content_url_path: The path part of the URL requested by the client.
    • content_url_query_params: The query parameters of the URL requested by the client. The query parameters are passed as a single string.
    • hostname: The name of the host that the request was sent to.
    • user_agent: The user agent string in the HTTP request from the client.
  • Subnet: Tests if a client’s IP belongs to a named subnet, see Subnets for more details.
  • User agent: Matches the given pattern against the user agent string in the HTTP request from the client. The match can be either a case-insensitive wildcard match or a regular expression match.

A session group may have more than one classifier. If it does, all the classifiers must match the incoming client request for it to belong to the session group. It is also possible for a request to belong to multiple session groups, or to none.

To send certain clients to a specific host you first need to create a suitable classifier using confcli in wizard mode. The wizard will guide you through the process of creating a new entry, asking you what value to input for each field and helping you by telling you what inputs are allowed for restricted fields such as the string comparison source mentioned above:

$ confcli services.routing.classifiers -w
Running wizard for resource 'classifiers'

Hint: Hitting return will set a value to its default.
Enter '?' to receive the help string

classifiers : [
  classifier can be one of
    1: anonymousIp
    2: asnIds
    3: contentUrlPath
    4: contentUrlQueryParameters
    5: geoip
    6: hostName
    7: ipranges
    8: random
    9: regexMatcher
    10: requestHeader
    11: stringMatcher
    12: subnet
    13: userAgent
  Choose element index or name: anonymousIp
  Adding a 'anonymousIp' element
    classifier : {
      name (default: ): anon_ip_matcher
      type (default: anonymousIp):
      inverted (default: False):
    }
  Add another 'classifier' element to array 'classifiers'? [y/N]: n
]
Generated config:
{
  "classifiers": [
    {
      "name": "anon_ip_matcher",
      "type": "anonymousIp",
      "inverted": false
    }
  ]
}
Merge and apply the config? [y/n]: y
  
$ confcli services.routing.classifiers -w
Running wizard for resource 'classifiers'

Hint: Hitting return will set a value to its default.
Enter '?' to receive the help string

classifiers : [
  classifier can be one of
    1: anonymousIp
    2: asnIds
    3: contentUrlPath
    4: contentUrlQueryParameters
    5: geoip
    6: hostName
    7: ipranges
    8: random
    9: regexMatcher
    10: requestHeader
    11: stringMatcher
    12: subnet
    13: userAgent
  Choose element index or name: asnIds
  Adding a 'asnIds' element
    classifier : {
      name (default: ): asn_matcher
      type (default: asnIds): ⏎
      inverted (default: False): ⏎
      asnIds <The list of ASN IDs to accept. (default: [])>: [
        asnId: 1
        Add another 'asnId' element to array 'asnIds'? [y/N]: y
        asnId: 2
        Add another 'asnId' element to array 'asnIds'? [y/N]: y
        asnId: 3
        Add another 'asnId' element to array 'asnIds'? [y/N]: ⏎
      ]
    }
  Add another 'classifier' element to array 'classifiers'? [y/N]: ⏎
]
Generated config:
{
  "classifiers": [
    {
      "name": "asn_matcher",
      "type": "asnIds",
      "inverted": false,
      "asnIds": [
        1,
        2,
        3
      ]
    }
  ]
}
Merge and apply the config? [y/n]: y
  
$ confcli services.routing.classifiers -w
Running wizard for resource 'classifiers'

Hint: Hitting return will set a value to its default.
Enter '?' to receive the help string

classifiers : [
  classifier can be one of
    1: anonymousIp
    2: asnIds
    3: contentUrlPath
    4: contentUrlQueryParameters
    5: geoip
    6: hostName
    7: ipranges
    8: random
    9: regexMatcher
    10: requestHeader
    11: stringMatcher
    12: subnet
    13: userAgent
  Choose element index or name: contentUrlPath
  Adding a 'contentUrlPath' element
    classifier : {
      name (default: ): vod_matcher
      type (default: contentUrlPath): ⏎
      inverted (default: False): ⏎
      patternType (default: stringMatch): ⏎
      pattern (default: ): *vod*
    }
  Add another 'classifier' element to array 'classifiers'? [y/N]: n
]
Generated config:
{
  "classifiers": [
    {
      "name": "vod_matcher",
      "type": "contentUrlPath",
      "inverted": false,
      "patternType": "stringMatch",
      "pattern": "*vod*"
    }
  ]
}
Merge and apply the config? [y/n]: y
  
$ confcli services.routing.classifiers -w
Running wizard for resource 'classifiers'

Hint: Hitting return will set a value to its default.
Enter '?' to receive the help string

classifiers : [
  classifier can be one of
    1: anonymousIp
    2: asnIds
    3: contentUrlPath
    4: contentUrlQueryParameters
    5: geoip
    6: hostName
    7: ipranges
    8: random
    9: regexMatcher
    10: requestHeader
    11: stringMatcher
    12: subnet
    13: userAgent
  Choose element index or name: contentUrlQueryParameters
  Adding a 'contentUrlQueryParameters' element
    classifier : {
      name (default: ): bitrate_matcher
      type (default: contentUrlQueryParameters): ⏎
      inverted (default: False): ⏎
      patternType (default: stringMatch): regex
      pattern (default: ): .*bitrate=100000.*
    }
  Add another 'classifier' element to array 'classifiers'? [y/N]: n
]
Generated config:
{
  "classifiers": [
    {
      "name": "bitrate_matcher",
      "type": "contentUrlQueryParameters",
      "inverted": false,
      "patternType": "regex",
      "pattern": ".*bitrate=100000.*"
    }
  ]
}
Merge and apply the config? [y/n]: y
  
$ confcli services.routing.classifiers -w
Running wizard for resource 'classifiers'

Hint: Hitting return will set a value to its default.
Enter '?' to receive the help string

classifiers : [
  classifier can be one of
    1: anonymousIp
    2: asnIds
    3: contentUrlPath
    4: contentUrlQueryParameters
    5: geoip
    6: hostName
    7: ipranges
    8: random
    9: regexMatcher
    10: requestHeader
    11: stringMatcher
    12: subnet
    13: userAgent
  Choose element index or name: geoip
  Adding a 'geoip' element
    classifier : {
      name (default: ): sweden_matcher
      type (default: geoip): ⏎
      inverted (default: False): ⏎
      continent (default: ): ⏎
      country (default: ): sweden
      cities : [
        city (default: ): ⏎
        Add another 'city' element to array 'cities'? [y/N]: ⏎
      ]
      asn (default: ): ⏎
    }
  Add another 'classifier' element to array 'classifiers'? [y/N]: ⏎
]
Generated config:
{
  "classifiers": [
    {
      "name": "sweden_matcher",
      "type": "geoip",
      "inverted": false,
      "continent": "",
      "country": "sweden",
      "cities": [
        ""
      ],
      "asn": ""
    }
  ]
}
Merge and apply the config? [y/n]: y
$ confcli services.routing.classifiers -w
Running wizard for resource 'classifiers'

Hint: Hitting return will set a value to its default.
Enter '?' to receive the help string

classifiers : [
  classifier can be one of
    1: anonymousIp
    2: asnIds
    3: contentUrlPath
    4: contentUrlQueryParameters
    5: geoip
    6: hostName
    7: ipranges
    8: random
    9: regexMatcher
    10: requestHeader
    11: stringMatcher
    12: subnet
    13: userAgent
  Choose element index or name: hostName
  Adding a 'hostName' element
    classifier : {
      name (default: ): host_name_classifier
      type (default: hostName): ⏎
      inverted (default: False): ⏎
      patternType (default: stringMatch): ⏎
      pattern (default: ): *live.example*
    }
  Add another 'classifier' element to array 'classifiers'? [y/N]: n
]
Generated config:
{
  "classifiers": [
    {
      "name": "host_name_classifier",
      "type": "hostName",
      "inverted": false,
      "patternType": "stringMatch",
      "pattern": "*live.example*"
    }
  ]
}
Merge and apply the config? [y/n]: y
  
$ confcli services.routing.classifiers -w
Running wizard for resource 'classifiers'

Hint: Hitting return will set a value to its default.
Enter '?' to receive the help string

classifiers : [
  classifier can be one of
    1: anonymousIp
    2: asnIds
    3: contentUrlPath
    4: contentUrlQueryParameters
    5: geoip
    6: hostName
    7: ipranges
    8: random
    9: regexMatcher
    10: requestHeader
    11: stringMatcher
    12: subnet
    13: userAgent
  Choose element index or name: ipranges
  Adding a 'ipranges' element
    classifier : {
      name (default: ): company_matcher
      type (default: ipranges): ⏎
      inverted (default: False): ⏎
      ipranges : [
        iprange (default: ): 90.128.0.0/12
        Add another 'iprange' element to array 'ipranges'? [y/N]: ⏎
      ]
    }
  Add another 'classifier' element to array 'classifiers'? [y/N]: ⏎
]
Generated config:
{
  "classifiers": [
    {
      "name": "company_matcher",
      "type": "ipranges",
      "inverted": false,
      "ipranges": [
        "90.128.0.0/12"
      ]
    }
  ]
}
Merge and apply the config? [y/n]: y
  
$ confcli services.routing.classifiers -w
Running wizard for resource 'classifiers'

Hint: Hitting return will set a value to its default.
Enter '?' to receive the help string

classifiers : [
  classifier can be one of
    1: anonymousIp
    2: asnIds
    3: contentUrlPath
    4: contentUrlQueryParameters
    5: geoip
    6: hostName
    7: ipranges
    8: random
    9: regexMatcher
    10: requestHeader
    11: stringMatcher
    12: subnet
    13: userAgent
  Choose element index or name: random
  Adding a 'random' element
    classifier <A classifier randomly applying to clients based on the provided probability. (default: OrderedDict())>: {
      name (default: ): random_matcher
      type (default: random):
      probability (default: 0.5): 0.7
    }
  Add another 'classifier' element to array 'classifiers'? [y/N]: n
]
Generated config:
{
  "classifiers": [
    {
      "name": "random_matcher",
      "type": "random",
      "probability": 0.7
    }
  ]
}
Merge and apply the config? [y/n]: y
  
$ confcli services.routing.classifiers -w
Running wizard for resource 'classifiers'

Hint: Hitting return will set a value to its default.
Enter '?' to receive the help string

classifiers : [
  classifier can be one of
    1: anonymousIp
    2: asnIds
    3: contentUrlPath
    4: contentUrlQueryParameters
    5: geoip
    6: hostName
    7: ipranges
    8: random
    9: regexMatcher
    10: requestHeader
    11: stringMatcher
    12: subnet
    13: userAgent
  Choose element index or name: regexMatcher
  Adding a 'regexMatcher' element
    classifier : {
      name (default: ): content_matcher
      type (default: regexMatcher): ⏎
      inverted (default: False): ⏎
      source (default: content_url_path): ⏎
      pattern (default: ): .*/(live|news_channel)/.*m3u8
    }
  Add another 'classifier' element to array 'classifiers'? [y/N]: ⏎
]
Generated config:
{
  "classifiers": [
    {
      "name": "content_matcher",
      "type": "regexMatcher",
      "inverted": false,
      "source": "content_url_path",
      "pattern": ".*/(live|news_channel)/.*m3u8"
    }
  ]
}
Merge and apply the config? [y/n]: y
  
$ confcli services.routing.classifiers -w
Running wizard for resource 'classifiers'

Hint: Hitting return will set a value to its default.
Enter '?' to receive the help string

classifiers : [
  classifier can be one of
    1: anonymousIp
    2: asnIds
    3: contentUrlPath
    4: contentUrlQueryParameters
    5: geoip
    6: hostName
    7: ipranges
    8: random
    9: regexMatcher
    10: requestHeader
    11: stringMatcher
    12: subnet
    13: userAgent
  Choose element index or name: requestHeader
  Adding a 'requestHeader' element
    classifier <A classifier that matches on headers in the HTTP request. (default: OrderedDict())>: {
      name (default: ): curl
      type (default: requestHeader): ⏎
      inverted (default: False): ⏎
      header (default: ): User-Agent
      patternType (default: stringMatch): ⏎
      patternSource (default: inline): ⏎
      pattern (default: ): curl*
    }
  Add another 'classifier' element to array 'classifiers'? [y/N]: n
]
Generated config:
{
  "classifiers": [
    {
      "name": "curl",
      "type": "requestHeader",
      "inverted": false,
      "header": "User-Agent",
      "patternType": "stringMatch",
      "patternSource": "inline",
      "pattern": "curl*"
    }
  ]
}
Merge and apply the config? [y/n]: y
  
$ confcli services.routing.classifiers -w
Running wizard for resource 'classifiers'

Hint: Hitting return will set a value to its default.
Enter '?' to receive the help string

classifiers : [
  classifier can be one of
    1: anonymousIp
    2: asnIds
    3: contentUrlPath
    4: contentUrlQueryParameters
    5: geoip
    6: hostName
    7: ipranges
    8: random
    9: regexMatcher
    10: requestHeader
    11: stringMatcher
    12: subnet
    13: userAgent
  Choose element index or name: stringMatcher
  Adding a 'stringMatcher' element
    classifier : {
      name (default: ): apple_matcher
      type (default: stringMatcher): ⏎
      inverted (default: False): ⏎
      source (default: content_url_path): user_agent
      pattern (default: ): *apple*
    }
  Add another 'classifier' element to array 'classifiers'? [y/N]: ⏎
]
Generated config:
{
  "classifiers": [
    {
      "name": "apple_matcher",
      "type": "stringMatcher",
      "inverted": false,
      "source": "user_agent",
      "pattern": "*apple*"
    }
  ]
}
Merge and apply the config? [y/n]: y
  
$ confcli services.routing.classifiers -w
Running wizard for resource 'classifiers'

Hint: Hitting return will set a value to its default.
Enter '?' to receive the help string

classifiers : [
  classifier can be one of
    1: anonymousIp
    2: asnIds
    3: contentUrlPath
    4: contentUrlQueryParameters
    5: geoip
    6: hostName
    7: ipranges
    8: random
    9: regexMatcher
    10: requestHeader
    11: stringMatcher
    12: subnet
    13: userAgent
  Choose element index or name: subnet
  Adding a 'subnet' element
    classifier : {
      name (default: ): company_matcher
      type (default: subnet): ⏎
      inverted (default: False): ⏎
      patternSource (default: inline): ⏎
      pattern (default: ): company
    }
  Add another 'classifier' element to array 'classifiers'? [y/N]: ⏎
]
Generated config:
{
  "classifiers": [
    {
      "name": "company_matcher",
      "type": "subnet",
      "inverted": false,
      "patternSource": "inline",
      "pattern": "company"
    }
  ]
}
Merge and apply the config? [y/n]: y
  
$ confcli services.routing.classifiers -w
Running wizard for resource 'classifiers'

Hint: Hitting return will set a value to its default.
Enter '?' to receive the help string

classifiers : [
  classifier can be one of
    1: anonymousIp
    2: asnIds
    3: contentUrlPath
    4: contentUrlQueryParameters
    5: geoip
    6: hostName
    7: ipranges
    8: random
    9: regexMatcher
    10: requestHeader
    11: stringMatcher
    12: subnet
    13: userAgent
  Choose element index or name: userAgent
  Adding a 'userAgent' element
    classifier : {
      name (default: ): iphone_matcher
      type (default: userAgent): ⏎
      inverted (default: False): ⏎
      patternType (default: stringMatch): regex
      pattern (default: ): i(P|p)hone
    }
  Add another 'classifier' element to array 'classifiers'? [y/N]: n
]
Generated config:
{
  "classifiers": [
    {
      "name": "iphone_matcher",
      "type": "userAgent",
      "inverted": false,
      "patternType": "regex",
      "pattern": "i(P|p)hone"
    }
  ]
}
Merge and apply the config? [y/n]: y
  

These classifiers can now be used to construct session groups and properly classify clients. Using the examples above, let’s create a session group classifying clients from Sweden using an Apple device:

$ confcli services.routing.sessionGroups -w
Running wizard for resource 'sessionGroups'

Hint: Hitting return will set a value to its default.
Enter '?' to receive the help string

sessionGroups : [
  sessionGroup : {
    name (default: ): inSwedenUsingAppleDevice
    classifiers : [
      classifier (default: ): 'sweden_matcher' and 'apple_matcher'
      Add another 'classifier' element to array 'classifiers'? [y/N]: ⏎
    ]
  }
  Add another 'sessionGroup' element to array 'sessionGroups'? [y/N]: ⏎
]
Generated config:
{
  "sessionGroups": [
    {
      "name": "inSwedenUsingAppleDevice",
      "classifiers": [
        "'sweden_matcher' and 'apple_matcher'"
      ]
    }
  ]
}
Merge and apply the config? [y/n]: y

Clients classified by the sweden_matcher and apple_matcher classifiers will now be put in the session group inSwedenUsingAppleDevice. Note the single quotation marks around the classifier names.

Using session groups in routing will be demonstrated later in this document.

Classifier Boolean Logic

Session groups combine classifiers using boolean logic expressions. Each classifier is referenced by its name enclosed in single quotes (e.g. 'sweden_matcher') and multiple classifiers can be combined using the keywords and, or and not to create sophisticated matching rules.

Operator precedence order:

  1. “Parentheses” ( ) group items together; expressions within parentheses are evaluated first.
  2. “not” negates the immediately following classifier or parenthesis expression
  3. “and” evaluates to true if both expressions surrounding it are true
  4. “or” evaluates to true if either expression surrounding it is true

Since “and” is higher than “or” the expression 'live' and 'spain' or 'sweden' is functionally identical to ('live' and 'spain') or 'sweden'. If you want the session group to accept any live content that is in either Spain or Sweden you need to write 'live' and ('spain' or 'sweden').

To match against all incoming traffic requesting live content but coming from outside of either Spain or Sweden, you’d write 'live' and not ('spain' or 'sweden').

It is permitted to nest several levels of parentheses and conjunctions if necessary to achieve the desired match.

Alternative form:

The "classifiers" property on a session group is an array, and each expression in it must be true for the session group to trigger. This means that "classifiers": ["('sweden' or 'spain') and 'live'"] is equivalent to "classifiers": ["'sweden' or 'spain'", "'live'"].

This fact can be used for example if expressions become so long that it is necessary to break them up into smaller parts in order to be able to read them.

Pattern Source

The requestHeader and subnet classifiers have a patternSource field, which can be either inline or selectionInput. When set to inline, the pattern is taken directly from the pattern field.

If it is selectionInput, the pattern field is used as a path in the selection input that points to the pattern to use for classification. The selection input path may contain a wildcard ("*"), which matches all elements inside an object or array.

For example, if patternSource contains /blocked_user_agents/*/agent, the classifier will take its pattern from all agent fields in objects inside /blocked_user_agents.

If the selection input contains the following data:

{
  "blocked_user_agents": {
    { "agent1": { "agent": "Firefox" }},
    { "agent2": { "agent": "Chrome" }}
  }
}

then the classifier will match either Firefox or Chrome.

Pattern Type

Several classifiers support pattern matching using either regular expressions or a simpler wildcard string matcher.

stringMatch:

The stringMatch pattern type uses shell-style wildcard matching for simple, readable patterns. Matching is case-insensitive.

Supported Wildcards:

  • * - Matches any sequence of characters (including none)
    • *vod* matches /vod/, /content/vod/stream.m3u8, /my-vod-content
    • /live/*/manifest.m3u8 matches /live/sports/manifest.m3u8, /live/news/manifest.m3u8
  • ? - Matches exactly one character
    • /video?.mp4 matches /video1.mp4, /videoA.mp4, but not /video10.mp4
    • /test/?????/file matches any path with exactly 5 characters between /test/ and /file
  • [characters] - Matches any single character from the set
    • [abc] matches a, b, or c
    • [0-9] matches any single digit
    • /video[12].mp4 matches /video1.mp4 or /video2.mp4
  • [!characters] or [^characters] - Matches any single character NOT in the set
    • [!xyz] matches any character except x, y, or z
    • [^0-9] matches any non-digit character
    • /file[!0-9].txt matches /fileA.txt but not /file3.txt
  • [[:class:]] - POSIX character classes for matching character types
    • [[:alnum:]] - alphanumeric (letters and digits)
    • [[:alpha:]] - letters only
    • [[:digit:]] - digits only
    • [[:lower:]] - lowercase letters
    • [[:upper:]] - uppercase letters
    • [[:space:]] - whitespace characters
    • Example: /user[[:digit:]][[:digit:]]/file matches /user12/file, /user00/file
  • \ (backslash) - Escapes special characters to match them literally
    • file\*.txt matches the literal string file*.txt (asterisk is not a wildcard)
    • test\?mark matches test?mark (question mark is not a wildcard)

regex:

The regex pattern type uses full regular expression matching for complex patterns. Matching is case-sensitive and follows the ECMAScript regular expression syntax (the same syntax used by JavaScript).

Common Examples:

  • .*\.m3u8$ - matches paths ending with .m3u8
  • .*/(?:live|vod)/.* - matches paths containing /live/ or /vod/
  • ^/api/v[0-9]+/.* - matches paths starting with /api/v1/, /api/v2/, etc.
  • .*bitrate=[0-9]{6,}.* - matches query strings with bitrate= followed by 6+ digits

When to use:

Use regex when you need:

  • Alternation (a|b)
  • Precise quantifiers ({n,m})
  • Anchoring (^ start, $ end)
  • Non-capturing groups ((?:...))
  • Lookaheads or other advanced features

For simple wildcard matching, stringMatch is more readable and performs better.

The complete ECMAScript regular expression syntax is documented at: