Examples
This section provides example scripts for common use cases. It includes the following examples:
- Select content routes based on URI string matches. See Select content routes based on URI string matches
- Rewrite the HTTP request host header and path. See Rewrite the HTTP request host header and path
- Rewrite the HTTP response Location header. See Rewrite the HTTP response Location header
- Redirect HTTP to HTTPS using Lua string substitution. See Redirect HTTP to HTTPS using Lua string substitution
- Redirect mobile users to the mobile version of a website. See Redirect mobile users to the mobile version of a website
- Insert random message ID into a header. See Insert random message ID into a header
- General HTTP redirect. See General HTTP redirect
- Use request headers in other events. See Use request headers in other events
- Compare IP address to address group. See Compare IP address to address group
- Redirect HTTP to HTTPS. See Redirect HTTP to HTTPS
- Rewrite HTTP to HTTPS in location. See Rewrite HTTP to HTTPS in location
- Rewrite HTTP to HTTPS in referer. See Rewrite HTTP to HTTPS in referer
- Rewrite HTTPS to HTTP in location. See Rewrite HTTPS to HTTP in location
- Rewrite HTTPS to HTTP in referer. See Rewrite HTTPS to HTTP in referer
- Fetch data from HTTP events. See Fetch data from HTTP events
- Replace HTTP body data. See Replace HTTP body data
- Persist and post_persist. See Persist and Post_persist
- Run multiple scripts. See Run multiple scripts
- Prioritize scripts. See Prioritize scripts
Tip: The examples show debug strings. Debug strings can be written to the console when the event is triggered. This is helpful when you are testing your scripts. diagnose debug enable diagnose debug module httproxy scripting |
Select content routes based on URI string matches
The content routing feature has rules that match HTTP requests to content routes based on a Boolean AND combination of match conditions. If you want to select routes based on a Boolean OR, you can configure multiple rules. The content routing rules table is consulted from top to bottom until one matches.
In some cases, it might be simpler to get the results you want using a script. In the following example, each rule selects content routes based on OR match conditions.
Content routing example
when RULE_INIT {
debug("get header init 1\n")
}
when HTTP_REQUEST{
uri = HTTP:uri_get()
if uri:find("sports") or uri:find("news") or uri:find("government") then
LB:routing("sp2")
debug("uri %s matches sports|news|government\n", uri);
elseif uri:find("finance") or uri:find("technology") or uri:find("shopping") then
LB:routing("sp3")
debug("uri %s matches finance|technology|shopping\n", uri);
elseif uri:find("game") or uri:find("bbs") or uri:find("testing") then
LB:routing("sp4")
debug("uri %s matches game|bbs|testing\n", uri);
elseif uri:find("billing") or uri:find("travel") or uri:find("weibo") then
LB:routing("sp5")
debug("uri %s matches billing|travel|weibo\n", uri);
else
debug("no matches for uri: %s \n", uri);
end
}
To use a script for content routing:
- Create the content route configuration objects. In the example above, sp2, sp3, sp4, and sp4 are the names of the content route configuration objects. You do not need to configure matching conditions for the content routes, however, because the script does the content matching.
- Create a script that matches content to the content route configuration objects, as shown above. Create a configuration object for the script.
- In the virtual server configuration:
- Enable content routing and select the content route configuration objects.
- Select the script.
Rewrite the HTTP request host header and path
You can use the content rewriting feature to rewrite the HTTP request Host header or the HTTP request URL. If you need more granular capabilities, you can use scripts. The following example rewrites the HTTP Host header and path.
Rewrite the HTTP Host header and path in a HTTP request
when RULE_INIT {
debug("rewrite the HTTP Host header and path in a HTTP request \n")
}
when HTTP_REQUEST{
host = HTTP:header_get_value("Host")
path = HTTP:path_get()
if host:lower():find("myold.hostname.com") then
debug("found myold.hostname.com in Host %s \n", host)
HTTP:header_replace("Host", "mynew.hostname.com")
HTTP:path_set("/other.html")
end
}
Note: You might find it useful to use a combination of string manipulation functions. For example, this script uses lower() to convert the Host strings to lowercase in combination with find(), which searches for the Host header for a match: host:lower():find("myold.hostname.com").
Rewrite the HTTP response Location header
You can use the content rewriting feature to rewrite the HTTP response Location header. If you are more comfortable using Lua string substitution, you can write a script to get the results you want. The following example rewrites the HTTP response Location header.
Rewrite the HTTP body in the response
when RULE_INIT {
debug("rewrite the HTTP response replacing myold.hostname.com with mynew.hostname.com \n")
}
when HTTP_RESPONSE{
location = HTTP:header_get_value("Location")
if location:lower():find("myold.hostname.com") then
debug("found myold.hostname.com in Location %s \n", location)
HTTP:header_replace("Location", "mynew.hostname.com")
end
}
Redirect HTTP to HTTPS using Lua string substitution
You can use the content rewriting feature to redirect an HTTP request to an HTTPS URL that has the same host and request URL using a PCRE regular expression. If you are more comfortable using Lua string substitution, you can write a script to get the results you want. The following example redirects users to the HTTPS location.
Redirect HTTP to HTTPS
when RULE_INIT {
debug("http to https redirect\n")
}
when HTTP_REQUEST{
host = HTTP:header_get_value("Host")
path = HTTP:path_get()
HTTP:redirect("https://%s%s",host,path);
}
Redirect mobile users to the mobile version of a website
The content rewriting feature does not support matching the User-Agent header. You can write a script that detects User-Agent headers that identify mobile device users and redirect them to the mobile version of a website.
Redirect mobile users to the mobile version of a website by parsing the User-Agent header
when RULE_INIT {
debug("detect User-Agent and go to mobile site\n")
}
when HTTP_REQUEST{
path = HTTP:path_get()
debug("path=%s\n",path)
agent = HTTP:header_get_value("User-Agent")
if agent:lower():find("iphone") or agent:lower():find("ipad") then
debug("found iphone or ipad in User-Agent %s \n", agent)
HTTP:redirect("https://m.mymobilesite.com%s",path)
end
}
Insert random message ID into a header
FortiADC offers the feature to insert messages and message IDs into HTTP request headers.
when HTTP_REQUEST{
ID=HTTP:rand_id()-- a 32-long string of HEX symbols
HTTP:header_insert("Message-ID",ID)
}
General HTTP redirect
You can redirect both HTTP requests and HTTP responses to a given location.
GENERAL_REDIRECT_DEMO:
when HTTP_REQUEST{
--can be used in both HTTP_REQUEST and HTTP_RESPONSE
--code and cookie are optional, code can be 301, 302, 303, 307, 308, if missed, 302 is used
t={}
t["code"] = 302;
t["url"] = "www.example.com"
t["cookie"] = "name=value; Expires=Wed, 09 Jun 2021 10:18:14 GMT"
HTTP:redirect_t(t);
}
Use request headers in other events
You can get stored request headers by using the session ID.
when RULE_INIT{
--initialize the global so-called "environment" variable
env={}
}
when HTTP_REQUEST{
sess_id = HTTP:get_session_id()
req={}
--store whatever you want to req, take url as an example
req["url"] = HTTP_uri_get()
env[id] = req
}
when HTTP_RESPONSE{
sess_id = HTTP:get_session_id()
req = env[id]
--now you can access the stored request headers
debug("my stored request url is %s\n", req["url"]);
}
when HTTP_DATA_REQUEST{
sess_id = HTTP:get_session_id()
req = env[id]
--now you can access the stored request headers
debug("my stored request url is %s\n", req["url"]);
}
when HTTP_DATA_RESPONSE{
sess_id = HTTP:get_session_id()
req = env[id]
--now you can access the stored request headers
debug("my stored request url is %s\n", req["url"]);
}
Compare IP address to address group
You can compare IP addresses to an internal list of IP addresses. The script will return different results signifying whether the IP is in the list.
when RULE_INIT{
--initialize the address group here
--for IPv4 address, mask can be a number between 0 to 32 or a dotted format
--support both IPv4 and IPv6, for IPv6, the mask is a number between 0 and 128
addr_group = "192.168.1.0/24"
addr_group = addr_group..",172.30.1.0/255.255.0.0"
addr_group = addr_group..",::ffff:172.40.1.0/120"
}
when HTTP_REQUEST{
client_ip = HTTP:client_addr()
matched = cmp_addr(client_ip, addr_group)
if matched then
debug("client ip found in address group\n");
else
debug("client ip not in address group\n");
end
}
Redirect HTTP to HTTPS
You can redirect an HTTP request from an HTTP location to an HTTPS location.
when HTTP_REQUEST{
Host = HTTP:header_get_value("host")
Url = HTTP:uri_get()
HTTP:redirect("https://%s%s", Host, Url)
}
Rewrite HTTP to HTTPS in location
You can rewrite HTTP request headers to replace all HTTP addresses with HTTPS addresses in the redirect location.
when HTTP_RESPONSE{
loc = HTTP:header_get_value("Location")
if loc then
newloc = string.gsub(loc, "http", "https") --replace all http by https in the redirect location
HTTP:header_replace("Location", newloc);
end
}
Rewrite HTTP to HTTPS in referer
You can rewrite HTTP request headers to replace all HTTP addresses with HTTPS addresses in the redirect referer.
when HTTP_RESPONSE{
ref = HTTP:header_get_value("Referer")
if ref then
newref = string.gsub(ref, "http", "https") --replace all http by https in the referer header
HTTP:header_replace("Referer", newref);
end
}
Rewrite HTTPS to HTTP in location
You can rewrite HTTP request headers to replace all HTTPS addresses with HTTP addresses in the redirect location.
when HTTP_RESPONSE{
loc = HTTP:header_get_value("Location")
if loc then
newloc = string.gsub(loc, "https", "http") --replace all https by http in the redirect location
HTTP:header_replace("Location", newloc);
end
}
Rewrite HTTPS to HTTP in referer
You can rewrite HTTP request headers to replace all HTTPS addresses with HTTP addresses in the redirect referer.
when HTTP_RESPONSE{
ref = HTTP:header_get_value("Referer")
if ref then
newref = string.gsub(ref, "https", "http") --replace all https by http in the referer header
HTTP:header_replace("Referer", newref);
end
}
Fetch data from HTTP events
You can collect data from both HTTP request and HTTP response events. You can then manipulate this data.
when HTTP_REQUEST{
--HTTP:collect command can be used in both HTTP_REQUEST and HTTP_RESPONSE events
--size is optional, otherwise, it will collect up to the full length or when 1.25M is reached
t={}
t["size"] = 100;
HTTP:collect(t)
}
when HTTP_DATA_REQUEST{
--check the size of the content
t={};
t["operation"]="size";
sz=HTTP:payload(t);
debug("content size: %s\n", sz);
--fetch the collected content
--offset and size are optional
t={};
t["operation"]="content";
t["offset"] = 0;
t["size"] = sz;
ct=HTTP:payload(t);
debug("content: %s\n", ct);
--do your own manipulation on the collected content
--replace the collected content by your new data
--offset and size are optional
t={};
t["operation"]="set";
t["offset"] = 0;
t["size"] = sz;
t["data"]="NEW DATA to SEND";
ret = HTTP:payload(t);
debug("set ret %s\n", ret);
}
Replace HTTP body data
You can find, remove, and replace data in the body of an HTTP request.
when HTTP_REQUEST{
--HTTP:collect command can be used in both HTTP_REQUEST and HTTP_RESPONSE events
--size is optional, otherwise, it will collect up to the full length or when 1.25M is reached
t={}
t["size"] = 100;
HTTP:collect(t)
}
when HTTP_DATA_REQUEST{
--check the size of the content
t={};
t["operation"]="size";
sz=HTTP:payload(t);
debug("content size: %s\n", sz);
--find a string or a regular expression in the buffered data
--offset, size and scope are optional, if scope is missing, "all" is assumed
t={};
t["operation"]="find";
t["offset"] = 0;
t["size"] = sz;
t["scope"] = "all";-- or "first"
t["data"] = "your string or a regular expression to find";
if HTTP:payload(t) then
debug("found %d occurences\n", ret);
else
debug("not found\n");
end
--remove a string or a regular expression in the buffered data
--offset, size and scope are optional, if scope is missing, "all" is assumed
t={};
t["operation"]="remove";
t["offset"] = 0;
t["size"] = sz;
t["scope"] = "all";-- or "first"
t["data"] = "your string or a regular expression to find";
if HTTP:payload(t) then
debug("removed %d occurences\n", ret);
else
debug("not found\n");
end
--replace a string or a regular expression in the buffered data by a new string
--offset, size and scope are optional, if scope is missing, "all" is assumed
t={};
t["operation"]="replace";
t["offset"] = 0;
t["size"] = sz;
t["scope"] = "all";-- or "first"
t["data"] = "your string or a regular expression to find";
t["new_data"] = "your new data";
if HTTP:payload(t) then
debug("replaced %d occurences\n", ret);
else
debug("not found\n");
end
}
Persist
You can set the entry to the persist table and real server will be assigned after lookup
when RULE_INIT {
env={}
PROXY:init_stick_tbl_timeout(1000)
}
when PERSISTENCE {
debug("PERSIST \n");
t={};
t["operation"] = "get_valid_server";
ret_tbl = HTTP: persist(t);
if(ret_tbl) then
for srv, state in pairs(ret_tbl) do
debug("server %s status %s\n", srv, state);
end
end
t={};
t["operation"] = "save_tbl";
t["hash_value"]= "hash_str";
t["srv_name"]= "rsrv_70";
ret = HTTP: persist(t)
if ret then
debug("save table success\n");
else
debug("save table failed\n");
end
t={};
t["operation"] = "dump_tbl";
t["index"] = 0;
t["count"] = 500;
ret_tbl = HTTP: persist(t)
if(ret_tbl) then
for k, cnt in pairs(ret_tbl) do
debug(" hash %s srv_name %s\n", k, cnt)
end
end
t={};
t["hash_value"]= "hash_str";
ret = HTTP:lookup_tbl(t);
if ret then
debug("LOOKUP success\n");
else
debug("LOOKUP fail\n");
end
}
Post_persist
You can get the current assigned server in POST_PERSIT and assign real server you like by setting table and lookup in PERSISTENCE
when RULE_INIT {
env={}
PROXY:init_stick_tbl_timeout(1000)
}
when PERSISTENCE {
debug("PERSIST \n");
t={};
t["hash_value"]= "hash_str";
ret = HTTP:lookup_tbl(t);
if ret then
debug("LOOKUP success\n");
else
debug("LOOKUP fail\n");
end
}
when POST_PERSIST {
debug("POST PERSIST \n");
t={};
t["operation"] = "get_current_assigned_server"
ret_tbl = HTTP: persist(t)
if ret then
debug("assign to %s\n", ret_tbl);
else
debug("get_current_assigned_server failed\n");
end
t={};
t["operation"] = "save_tbl";
t["hash_value"]= "hash_str";
t["srv_name"]= "rsrv_70";
ret = HTTP: persist(t)
if ret then
debug("save table success\n");
else
debug("save table failed\n");
end
}
Run multiple scripts
You can run multiple scripts in FortiADC. When running multiple scripts, you may set a priority number for each script. FortiADC will run them in order from lowest priority to highest priority. The default priority is 500. if two scripts have the same priority number, they will be executed in the order in which they were added.
--script 1:
when HTTP_REQUEST priority 500 {
LB:routing(“cr1”)
}
--script 2:
when HTTP_RESPONSE priority 500 {
HTTP:close()
}
--script 3:
when HTTP_REQUEST priority 400 {
LB:routing(“cr2”)
}
--script 4:
when HTTP_RESPONSE priority 600 {
HTTP:close()
}
Prioritize scripts
While running multiple scripts, you can prioritize scripts. Add a priority number to each script when you create it, and FortiADC will run them in order from lowest priority to highest priority. The default priority is 500.If two scripts have the same priority number, they will be executed in the order in which they were added.
when RULE_INIT priority 14 {
--This is one of the script to demo the control of multiple scripts
--please change the priority of each event according to your need
debug("INIT in script 1\n");
}
when HTTP_REQUEST priority 12 {
debug("HTTP_REQUEST in script 1\n");
--add your own manipulation here
--you can disable rest of the HTTP_REQUEST events from executing by disabling this event
t={};
t["event"]="req"; -- can be "req", "res", "data_req", and "data_res"
t["operation"]="disable"; -- can be "enable", and "disable"
HTTP:set_event(t);
debug("disable rest of the HTTP_REQUEST events in script 1\n");
--you can also disable other events, say HTTP_RESPONSE, DATA events
--in the case of keep-alive, all events will be re-enabled automatically even though they are disabled in previous TRANSACTION using the HTTP:set_event(t) command. To disable this automatic re-enabling behavior, you can call HTTP:set_auto(t) as below
t={};
t["event"]="req"; -- can be "req", "res", "data_req", and "data_res"
t["operation"]="disable"; -- can be "enable", and "disable"
HTTP:set_auto(t);
debug("disable automatic re-enabling of the HTTP_REQUEST events in script 1\n");
--you can also disable automatic re-enabling for other events, say HTTP_RESPONSE, DATA events
}
or
when RULE_INIT priority 24 {
--This is one of the script to demo the control of multiple scripts
--please change the priority of each event according to your need
debug("INIT in script 2\n");
}
when HTTP_REQUEST priority 24 {
debug("HTTP_REQUEST in script 2\n");
--add your own manipulation here
--you can disable rest of the HTTP_REQUEST events from executing by disabling this event
t={};
t["event"]="req"; -- can be "req", "res", "data_req", and "data_res"
t["operation"]="disable"; -- can be "enable", and "disable"
HTTP:set_event(t);
debug("disable rest of the HTTP_REQUEST events in script 2\n");
--you can also disable other events, say HTTP_RESPONSE, DATA events
--in the case of keep-alive, all events will be re-enabled automatically even though they are disabled in previous TRANSACTION using the HTTP:set_event(t) command. To disable this automatic re-enabling behavior, you can call HTTP:set_auto(t) as below
t={};
t["event"]="req"; -- can be "req", "res", "data_req", and "data_res"
t["operation"]="disable"; -- can be "enable", and "disable"
HTTP:set_auto(t);
debug("disable automatic re-enabling of the HTTP_REQUEST events in script 2\n");
--you can also disable automatic re-enabling for other events, say HTTP_RESPONSE, DATA events
}