Adapt to multi-CDN

How to write ESB3024 Router configuration to handle multi-CDN setups with tokens.

This page describes how to write configuration for setups that require the URL sent back to the client to be rewritten based on routing results, for example by adding a token to the path or as a query string parameter. For configuration in general, see Configuration.

In order to change the redirect location URL, we need to add a response translation function to the confd configuration. By default, all translation functions are empty:

$ confcli services.routing.translationFunctions
{
    "translationFunctions": {
        "request": "",
        "response": ""
    }
}

For full documentation of the response translation function, please see Lua Hooks.

Simple example

Translation functions tend to become large, which makes it hard to write them inside a single string. Instead, the function can be written in a separate .lua script file that is uploaded to the router and referenced from the confd configuration.

The following example will take this approach, adding a script called "rewrite.lua" to the configured custom Lua folder /tmp/custom_lua on the router. For more information see the Configuration JSON Overview.

Lua script

The following script assumes the existence of the function parse_url that returns an object with URL segments that can be altered and then combined into a URL string using the tostring(obj) function again.

Beware that the router does not validate the URL in the Location header, it is up to the Lua script writer to ensure that only URLs are returned.

function response_rewrite(Headers)
   -- The live server has the token as part of the path, added first before the
   -- path from the request.
   local location = parse_url(response_headers.location)

   if location.host == "live-host.example" then
      location.path = "/live-prefix" .. location.path
      return HTTPResponse(
         {
            Headers = {
               {"location", tostring(location)}
            }
         }
      )
   end

   -- The VOD server expects the token as a query string rather than as part
   -- of the path segment.
   -- Since VOD can be used for offload, check that session_groups.is_vod
   -- is set as well.
   if location.host == "vod-host.example" and session_groups.is_vod then
      location.query["token"] = md5sum(secret_key .. request.path)
      return HTTPResponse(
         {
            Headers = {
               {"location", tostring(location)}
            }
         }
      )
   end

   -- If the request was neither in the is_live session group, nor contained
   -- the path element "vod", we return a nil object to indicate that we don't
   -- want to change anything in the response.
   return nil
end

Configuration

To configure a response translation function using with the custom Lua function response_rewrite() using confcli, run the command:

$ confcli services.routing.translationFunctions.response 'return response_rewrite()'
services.routing.translationFunctions.response = 'return response_rewrite()'

An example confd configuration utilizing this translation function based on session groups would look like this:

$ confcli services.routing
{
  "translationFunctions": {
    "request": "",
    "response": "return response_rewrite()"
  },
  "sessionGroups": [
    {
      "name": "is_live",
      "classifiers": [
          "is_live_classifier"
      ]
    },
    {
      "name": "is_vod",
      "classifiers": [
          "is_vod_classifier"
      ]
    }
  ],
  "classifiers": [
    {
      "name": "is_live_classifier",
      "type": "contentUrlPath",
      "inverted": false,
      "patternType": "stringMatch",
      "pattern": "*/live/*"
    },
    {
      "name": "is_vod_classifier",
      "type": "contentUrlPath",
      "inverted": false,
      "patternType": "stringMatch",
      "pattern": "*/vod/*"
    }
  ],
  "hostGroups": [
    {
      "name": "live-cdn",
      "type": "host",
      "httpPort": 80,
      "httpsPort": 443,
      "hosts": [
          {
              "name": "live-host",
              "hostname": "live-host.example",
              "ipv6_address": ""
          }
      ]
    },
    {
      "name": "vod-cdn",
      "type": "host",
      "httpPort": 80,
      "httpsPort": 443,
      "hosts": [
          {
              "name": "vod-host",
              "hostname": "vod-host.example",
              "ipv6_address": ""
          }
      ]
    }
  ],
  "rules": [
    {
      "name": "multiCdnExample",
      "type": "firstMatch",
      "targets": [
        {
          "condition": "in_session_group('is_live')",
          "onMatch": "live-host"
        },
        {
          "condition": "in_session_group('is_vod')",
          "onMatch": "vod-host"
        },
        // Use the vod-host as offload for any content, but don't set any
        // token for requests not classified as vod.
        {
          "condition": "always()",
          "onMatch": "vod-host"
        }
      ]
    }
  ],
  "entrypoint": "multiCdnExample",
  "applyConfig": true
}