Rhai Scripting

Avalon supports Rhai scripting for advanced request handling and URL rewriting. Rhai is a simple, safe scripting language embedded in Rust applications.

Basic Usage

[servers.routes.handle]
type = "script"
script = '''
// Access request information
let path = request.path;
let method = request.method;
let host = request.host;
let client_ip = request.client_ip;

// Return a response
response(200, "Hello, World!", #{})
'''

Request Object

PropertyTypeDescription
request.pathstringRequest path
request.methodstringHTTP method
request.hoststringRequest hostname
request.client_ipstringClient IP address
request.querystringQuery string
request.headersmapRequest headers (lowercase keys)

Built-in Functions

Response Functions

FunctionDescriptionExample
response(status, body, headers)Return custom responseresponse(200, "OK", #{})
json_response(data)Return JSON responsejson_response(#{ key: "value" })
redirect(url)302 redirectredirect("https://example.com")
redirect_with_code(url, code)Redirect with status coderedirect_with_code("/new", 301)

String Functions

FunctionDescriptionExample
url_encode(str)URL encodeurl_encode("hello world")
url_decode(str)URL decodeurl_decode("hello%20world")
base64_encode(str)Base64 encodebase64_encode("hello")
base64_decode(str)Base64 decodebase64_decode("aGVsbG8=")
regex_match(pattern, text)Regex matchregex_match("^/api/", path)
regex_replace(pattern, repl, text)Regex replaceregex_replace("old", "new", text)

JSON Functions

FunctionDescriptionExample
json_parse(str)Parse JSONjson_parse('{"a":1}')
json_stringify(obj)Stringify to JSONjson_stringify(#{ a: 1 })

Utility Functions

FunctionDescriptionExample
query_param(name)Get query parameterquery_param("page")
hash_md5(str)MD5 hashhash_md5("password")
hash_sha256(str)SHA256 hashhash_sha256("data")
time_now()Current Unix timestamptime_now()

Examples

API Gateway Routing

[servers.routes.handle]
type = "script"
script = '''
let path = request.path;
let api_key = request.headers["x-api-key"];

// Validate API key
if api_key != "secret-key-123" {
    response(401, json_stringify(#{ error: "Invalid API key" }), #{
        "Content-Type": "application/json"
    })
} else if path.starts_with("/v1/users") {
    // Forward to user service
    #{ proxy: "user-service:8080" }
} else if path.starts_with("/v1/orders") {
    // Forward to order service
    #{ proxy: "order-service:8080" }
} else {
    json_response(#{ error: "Not Found", path: path })
}
'''

A/B Testing

[servers.routes.handle]
type = "script"
script = '''
// Simple A/B test based on client IP
let ip_hash = hash_md5(request.client_ip);
let variant = if ip_hash.starts_with("0") || ip_hash.starts_with("1") {
    "A"
} else {
    "B"
};

response(200, "", #{
    "X-AB-Variant": variant,
    "Set-Cookie": "ab_variant=" + variant + "; Path=/; Max-Age=86400"
})
'''

Health Check Endpoint

[servers.routes.handle]
type = "script"
script = '''
if request.path == "/health" {
    json_response(#{
        status: "ok",
        timestamp: time_now(),
        version: "1.0.0"
    })
} else {
    response(404, "Not Found", #{})
}
'''

Request Logging

[servers.routes.handle]
type = "script"
script = '''
// Log request details
let log_entry = #{
    path: request.path,
    method: request.method,
    client_ip: request.client_ip,
    timestamp: time_now()
};

// Continue to proxy (this is a passthrough example)
#{ proxy: "backend:8080" }
'''

Rate Limit Response

[servers.routes.handle]
type = "script"
script = '''
// Check rate limit header
let remaining = request.headers["x-ratelimit-remaining"];

if remaining == "0" {
    response(429, json_stringify(#{
        error: "Too Many Requests",
        retry_after: 60
    }), #{
        "Content-Type": "application/json",
        "Retry-After": "60"
    })
} else {
    #{ proxy: "backend:8080" }
}
'''