Fortinet white logo
Fortinet white logo
8.0.0

Custom reply

Custom reply

These functions only can be used in HTTP client side event (only HTTP_REQUEST now).

HTTP:redirect (“fmt”, …)

Reply to client with redirect response.

Example
when HTTP_REQUEST {
    HTTP:redirect(“https://%s”, HTTP:host())
}

HTTP:reply (response)

Reply to client with custom response.

Argument response is a lua array. It includes:

  • status: Integer. Default is 200.

  • reason: String. If not set, the system will use the default value of status code. For example, if the status code is 200, the default value of reason is “OK”.

  • headers: Lua table. Each value of the table is a lua array. It contains all headers except “content-length”. “content-length” will be automatically set with the body size.

  • Body: String.

To be specific:

HTTP:reply{
status = 400,
reason = “test reason”,
headers = {
["content-type"] = { "text/html" },
["cache-control"] = { "no-cache", "no-store" },
},
body = "<html><body><h1>invalid request<h1></body></html>",
}
Example
function reply_invalid(HTTP)
    HTTP:reply{
        status = 400,
        headers = {
            ["content-type"] = { "text/html" },
            ["cache-control"] = { "no-cache", "no-store" },
        },
        body = "<html><body><h1>Invalid API Request<h1></body></html>",
    }
end
function check_ip_reputation(HTTP)
    local v = HTTP:arg("ip")
    if v then v = ip.addr(v) end -- convert string to ip
    if not v then return reply_invalid(HTTP) end
    local r = ip.reputation(v)
    local body = string.format("<html><body><h1>Reputation of IP %s: %s<h1></body></html>",
        v, #r > 0 and table.concat(r, ', ') or "No Found")
    HTTP:reply{
        status = 200,
        headers = {
            ["content-type"] = { "text/html" },
            ["cache-control"] = { "no-cache", "no-store" },
        },
        body = body,
    }    
end
function check_ip_geo(HTTP)
    local v = HTTP:arg("ip")
    if v then v = ip.addr(v) end -- convert string to ip
    if not v then return reply_invalid(HTTP) end
    local geo = ip.geo(v)
    local geo_code = ip.geo_code(v)
    local body = string.format("<html><body><h1>GEO of IP %s: %s, code: %s<h1></body></html>",
        v, geo, geo_code)
HTTP:reply{
    status = 200,
    headers = {
        ["content-type"] = { "text/html" },
        ["cache-control"] = { "no-cache", "no-store" },
    },
    body = body,
    }
end
when RULE_INIT {
    actions = {}
    actions["reputation"] = check_ip_reputation
    actions["geo"] = check_ip_geo
    convert = {}
    convert["testing"] = "test"
    convert["debugging"] = "debug"
    whitelist = {}
    whitelist["test"] = true
    whitelist["debug"] = true
    whitelist["others"] = true
}
when HTTP_REQUEST {
    local path = HTTP:path()
    path = path:gsub("^/api/", "/api2/") -- convert /api/ to /api2/
    local api = path:match("^/api2/(.+)") -- get api string that is after /api2/
    -- check api
    if api then
        if actions[api] then -- if api is in table "actions", run function
            return actions[api](HTTP)
        end
        if convert[api] then -- if api is in table "convert", convert api
            api = convert[api] -- change to new api
        end
        if not whitelist[api] then -- if api is not in whitelist, reply invalid
            return reply_invalid(HTTP)
        end
        HTTP:set_path("/api2/" .. api) -- pass the api to server
        return
    end
    -- if path doesn't starts with /api or /api2, do nothing
}

Custom reply

Custom reply

These functions only can be used in HTTP client side event (only HTTP_REQUEST now).

HTTP:redirect (“fmt”, …)

Reply to client with redirect response.

Example
when HTTP_REQUEST {
    HTTP:redirect(“https://%s”, HTTP:host())
}

HTTP:reply (response)

Reply to client with custom response.

Argument response is a lua array. It includes:

  • status: Integer. Default is 200.

  • reason: String. If not set, the system will use the default value of status code. For example, if the status code is 200, the default value of reason is “OK”.

  • headers: Lua table. Each value of the table is a lua array. It contains all headers except “content-length”. “content-length” will be automatically set with the body size.

  • Body: String.

To be specific:

HTTP:reply{
status = 400,
reason = “test reason”,
headers = {
["content-type"] = { "text/html" },
["cache-control"] = { "no-cache", "no-store" },
},
body = "<html><body><h1>invalid request<h1></body></html>",
}
Example
function reply_invalid(HTTP)
    HTTP:reply{
        status = 400,
        headers = {
            ["content-type"] = { "text/html" },
            ["cache-control"] = { "no-cache", "no-store" },
        },
        body = "<html><body><h1>Invalid API Request<h1></body></html>",
    }
end
function check_ip_reputation(HTTP)
    local v = HTTP:arg("ip")
    if v then v = ip.addr(v) end -- convert string to ip
    if not v then return reply_invalid(HTTP) end
    local r = ip.reputation(v)
    local body = string.format("<html><body><h1>Reputation of IP %s: %s<h1></body></html>",
        v, #r > 0 and table.concat(r, ', ') or "No Found")
    HTTP:reply{
        status = 200,
        headers = {
            ["content-type"] = { "text/html" },
            ["cache-control"] = { "no-cache", "no-store" },
        },
        body = body,
    }    
end
function check_ip_geo(HTTP)
    local v = HTTP:arg("ip")
    if v then v = ip.addr(v) end -- convert string to ip
    if not v then return reply_invalid(HTTP) end
    local geo = ip.geo(v)
    local geo_code = ip.geo_code(v)
    local body = string.format("<html><body><h1>GEO of IP %s: %s, code: %s<h1></body></html>",
        v, geo, geo_code)
HTTP:reply{
    status = 200,
    headers = {
        ["content-type"] = { "text/html" },
        ["cache-control"] = { "no-cache", "no-store" },
    },
    body = body,
    }
end
when RULE_INIT {
    actions = {}
    actions["reputation"] = check_ip_reputation
    actions["geo"] = check_ip_geo
    convert = {}
    convert["testing"] = "test"
    convert["debugging"] = "debug"
    whitelist = {}
    whitelist["test"] = true
    whitelist["debug"] = true
    whitelist["others"] = true
}
when HTTP_REQUEST {
    local path = HTTP:path()
    path = path:gsub("^/api/", "/api2/") -- convert /api/ to /api2/
    local api = path:match("^/api2/(.+)") -- get api string that is after /api2/
    -- check api
    if api then
        if actions[api] then -- if api is in table "actions", run function
            return actions[api](HTTP)
        end
        if convert[api] then -- if api is in table "convert", convert api
            api = convert[api] -- change to new api
        end
        if not whitelist[api] then -- if api is not in whitelist, reply invalid
            return reply_invalid(HTTP)
        end
        HTTP:set_path("/api2/" .. api) -- pass the api to server
        return
    end
    -- if path doesn't starts with /api or /api2, do nothing
}