### Running the Content Rewriting Example
Source: https://github.com/dip-proto/fastly.js/blob/master/docs/examples/content-rewriting.md
This command shows how to run the VCL content rewriting example locally using Fastly.js with Bun. It starts a local HTTP proxy server.
```bash
bun run index.ts content-rewriting.vcl
```
--------------------------------
### Running Fastly.js Access Control Example
Source: https://github.com/dip-proto/fastly.js/blob/master/docs/examples/access-control.md
Instructions on how to run the provided VCL access control example locally using Fastly.js and Bun. This command starts a local HTTP proxy server to test the defined rules.
```bash
bun run index.ts access-control.vcl
```
--------------------------------
### Running the Fastly.js Example
Source: https://github.com/dip-proto/fastly.js/blob/master/docs/examples/error-pages.md
This command shows how to run the VCL error handling example locally using Fastly.js with Bun.
```bash
bun run index.ts error-pages.vcl
```
--------------------------------
### Running the Fastly.js Example
Source: https://github.com/dip-proto/fastly.js/blob/master/docs/examples/load-balancing.md
Command to run the VCL load balancing example using Fastly.js with Bun. This command saves the VCL content to a file and executes it.
```bash
bun run index.ts load-balancing.vcl
```
--------------------------------
### Complete Restart Example
Source: https://github.com/dip-proto/fastly.js/blob/master/docs/reference/restart.md
Demonstrates URL normalization, authentication, and failover using restarts, including loop prevention.
```vcl
sub vcl_recv {
# Add a header to track restarts
set req.http.X-Restart-Count = req.restarts;
# URL normalization
if (req.restarts == 0 && req.url ~ "/$ ") {
set req.url = req.url + "index.html";
set req.http.X-Restart-Reason = "url_normalization";
restart;
}
# Authentication
if (req.restarts == 1 && !req.http.Authorization && req.http.Cookie ~ "auth_token=([^;]+)") {
set req.http.Authorization = "Bearer " + re.group.1;
set req.http.X-Restart-Reason = "auth";
restart;
}
# Failover
if (req.restarts == 2 && req.http.X-Backend-Status ~ "5\d\d") {
set req.backend = "fallback_backend";
set req.http.X-Restart-Reason = "failover";
restart;
}
# Prevent infinite loops
if (req.restarts >= 3) {
error 503 "Maximum number of restarts reached";
}
return(lookup);
}
```
--------------------------------
### VCL Goto Syntax Example
Source: https://github.com/dip-proto/fastly.js/blob/master/docs/goto.md
Demonstrates the basic syntax for defining a label and using the goto statement in VCL.
```vcl
# Define a label
label_name:
# Code to execute when jumping to this label
set req.http.X-Label = "label_name";
# Jump to a label
goto label_name;
```
--------------------------------
### Fastly.JS Proxy Server Output
Source: https://github.com/dip-proto/fastly.js/blob/master/docs/getting-started.md
Example output when the Fastly.JS proxy server starts, indicating loaded VCL files and configured backends.
```text
Loading VCL files: my-first-vcl.vcl
Initializing security module...
Setting up backends...
Backends configured: default, main, api, static
Directors configured: main_director, fallback_director
HTTP Proxy server running at http://127.0.0.1:8000
Using VCL files: my-first-vcl.vcl
```
--------------------------------
### Complete String Processing System Example
Source: https://github.com/dip-proto/fastly.js/blob/master/fastly-vcl/vcl-functions/standard_utility_functions.md
An integrated example showcasing multiple VCL utility functions for processing URLs, query parameters, client IP addresses, and building request tags. This system normalizes request data and adds informative headers.
```vcl
sub vcl_recv {
# Step 1: Extract and normalize URL components
declare local var.url_path STRING;
declare local var.url_directory STRING;
declare local var.url_filename STRING;
declare local var.url_extension STRING;
# Get URL path and normalize to lowercase
set var.url_path = std.tolower(req.url.path);
# Extract directory and filename
set var.url_directory = std.dirname(var.url_path);
set var.url_filename = std.basename(var.url_path);
# Extract file extension if present
if (var.url_filename ~ "\.([^.]+)$") {
set var.url_extension = re.group.1;
} else {
set var.url_extension = "";
}
# Step 2: Process query parameters
declare local var.page_param STRING;
declare local var.limit_param STRING;
declare local var.sort_param STRING;
declare local var.page INTEGER;
declare local var.limit INTEGER;
# Get and normalize query parameters
set var.page_param = querystring.get(req.url.qs, "page");
set var.limit_param = querystring.get(req.url.qs, "limit");
set var.sort_param = std.tolower(querystring.get(req.url.qs, "sort"));
# Convert page and limit to integers with validation
set var.page = var.page_param != "" ? std.atoi(var.page_param) : 1;
set var.limit = var.limit_param != "" ? std.atoi(var.limit_param) : 20;
# Validate and normalize values
if (var.page < 1) { set var.page = 1; }
if (var.limit < 1) { set var.limit = 1; }
if (var.limit > 100) { set var.limit = 100; }
# Step 3: Process client information
declare local var.client_ip_str STRING;
declare local var.xff STRING;
declare local var.real_client_ip STRING;
# Convert client IP to string
set var.client_ip_str = std.ip2str(client.ip);
# Process X-Forwarded-For header if present
set var.xff = req.http.X-Forwarded-For;
if (var.xff) {
# Extract the original client IP (first IP in the list)
if (var.xff ~ "^([^,]+)") {
set var.real_client_ip = re.group.1;
} else {
set var.real_client_ip = var.client_ip_str;
}
} else {
set var.real_client_ip = var.client_ip_str;
}
# Step 4: Build request tags for analytics
declare local var.request_tags STRING;
# Start with an empty list
set var.request_tags = "";
# Add tags based on request properties
# Method type
set var.request_tags = std.collect(var.request_tags, std.tolower(req.method));
# Content type
if (var.url_extension != "") {
set var.request_tags = std.collect(var.request_tags, var.url_extension);
}
# API version
if (var.url_directory ~ "^/api/v([0-9]+)") {
set var.request_tags = std.collect(var.request_tags, "api-v" + re.group.1);
} else if (var.url_directory ~ "^/api") {
set var.request_tags = std.collect(var.request_tags, "api");
}
# Authentication status
if (req.http.Cookie:session || req.http.Authorization) {
set var.request_tags = std.collect(var.request_tags, "auth");
} else {
set var.request_tags = std.collect(var.request_tags, "anon");
}
# Step 5: Set normalized request headers
set req.http.X-URL-Directory = var.url_directory;
set req.http.X-URL-Filename = var.url_filename;
set req.http.X-URL-Extension = var.url_extension;
set req.http.X-Page = var.page;
set req.http.X-Limit = var.limit;
set req.http.X-Sort = var.sort_param;
set req.http.X-Client-IP = var.client_ip_str;
set req.http.X-Real-Client-IP = var.real_client_ip;
set req.http.X-Request-Tags = var.request_tags;
set req.http.X-Tag-Count = std.count(var.request_tags);
}
```
--------------------------------
### Customizing VCL Context with Setup Data
Source: https://github.com/dip-proto/fastly.js/blob/master/docs/api/vcl-runtime.md
Illustrates how to pre-configure shared state like backends and directors in a setup context and then copy them to per-request contexts. This is the recommended method for seeding shared state in Fastly.JS.
```typescript
const setupContext = createVCLContext();
setupContext.std!.backend!.add("api", "httpbin.org", 80, false);
setupContext.std!.director!.add("main_director", "random", { quorum: 50, retries: 3 });
// per request:
const context = createVCLContext();
context.backends = { ...setupContext.backends };
context.directors = { ...setupContext.directors };
```
--------------------------------
### Install Dependencies
Source: https://github.com/dip-proto/fastly.js/blob/master/docs/getting-started.md
Install the necessary dependencies for Fastly.JS using the Bun package manager.
```bash
bun install
```
--------------------------------
### Dynamic Page Assembly Example
Source: https://github.com/dip-proto/fastly.js/blob/master/docs/features/edge-side-includes.md
A complete example showing VCL configuration to enable ESI and an HTML structure utilizing various ESI tags for dynamic content assembly.
```vcl
sub vcl_recv {
# Set the default backend
set req.backend = default;
return(lookup);
}
sub vcl_fetch {
# Enable ESI processing for HTML responses
if (beresp.http.Content-Type ~ "text/html") {
set beresp.do_esi = true;
}
return(deliver);
}
```
```html
My Website
```
--------------------------------
### Fastly.JS Proxy Server Output with Multiple VCLs
Source: https://github.com/dip-proto/fastly.js/blob/master/docs/getting-started.md
Example output when the Fastly.JS proxy server starts with multiple VCL files loaded, showing the concatenated list of files.
```text
Loading VCL files: common-settings.vcl, backends.vcl, caching-rules.vcl
Initializing security module...
Setting up backends...
Backends configured: default, main, api, static
Directors configured: main_director, fallback_director
HTTP Proxy server running at http://127.0.0.1:8000
Using VCL files: common-settings.vcl, backends.vcl, caching-rules.vcl
```
--------------------------------
### Run VCL Configuration
Source: https://github.com/dip-proto/fastly.js/blob/master/docs/getting-started.md
Execute your VCL configuration using the Bun runtime. This command starts the Fastly.JS proxy server.
```bash
bun run index.ts my-first-vcl.vcl
```
--------------------------------
### Basic VCL File Structure Example
Source: https://github.com/dip-proto/fastly.js/blob/master/docs/tutorials/01-basic-vcl-syntax.md
Illustrates the fundamental components of a VCL file: backend definitions, ACLs, tables, subroutines, and directors.
```vcl
# Backend definition
backend default {
.host = "example.com";
.port = "80";
}
# ACL definition
acl internal {
"127.0.0.1";
"192.168.0.0"/24;
}
# Table definition
table example_table {
"key1": "value1",
"key2": "value2",
}
# Subroutine definition
sub vcl_recv {
# Subroutine code here
}
# Director definition
director main_director random {
.quorum = 50%;
{ .backend = default; .weight = 1; }
}
```
--------------------------------
### Build Query String From Scratch
Source: https://github.com/dip-proto/fastly.js/blob/master/fastly-vcl/vcl-functions/query_string_functions.md
Placeholder for building a query string from scratch, indicating that further examples would follow to demonstrate this functionality.
```vcl
declare local var.built_qs STRING;
```
--------------------------------
### VCL Operators Examples
Source: https://github.com/dip-proto/fastly.js/blob/master/docs/tutorials/01-basic-vcl-syntax.md
Provides examples of using arithmetic, comparison, logical, regular expression, assignment, and concatenation operators in VCL.
```vcl
# Arithmetic
set var.result = 5 + 3 * 2;
# Comparison
if (req.http.User-Agent == "Mozilla/5.0") {
# Do something
}
# Logical
if (req.url ~ "^/api/" && req.method == "POST") {
# Do something
}
# Regular expression
if (req.url ~ "^/static/") {
# Do something
}
# Assignment
set req.http.X-Test = "value";
# Concatenation
set req.http.X-Full-URL = "https://" + req.http.Host + req.url;
```
--------------------------------
### Example: Backend with Custom Timeouts
Source: https://github.com/dip-proto/fastly.js/blob/master/fastly-vcl/04-backend-configuration.md
Configures a backend with custom connection and response timeouts. Adjust these values to accommodate slower origins or specific performance requirements.
```vcl
backend F_slow_origin {
.host = "api.example.com";
.port = "443";
.ssl = true;
.connect_timeout = 5s;
.first_byte_timeout = 30s;
.between_bytes_timeout = 20s;
}
```
--------------------------------
### Run Fastly.js Browser Playground
Source: https://github.com/dip-proto/fastly.js/blob/master/docs/getting-started.md
Starts the local browser playground for experimenting with VCL client-side. Note that RSA and JWT signature verification are not available in the browser.
```bash
bun run web
```
--------------------------------
### Run VCL Flow Control Example
Source: https://github.com/dip-proto/fastly.js/blob/master/README.md
Command to execute the VCL configuration using the Fastly.js proxy. Ensure the 'index.ts' file and the 'goto-flow.vcl' are in the correct locations.
```bash
bun run index.ts goto-flow.vcl
```
--------------------------------
### Combining Operators Example
Source: https://github.com/dip-proto/fastly.js/blob/master/docs/reference/vcl-operators.md
Demonstrates combining logical AND, OR, and regular expression operators with assignment and return statements.
```vcl
if ((req.method == "GET" || req.method == "HEAD") && req.url ~ "^/api/") {
set req.http.X-API = "true";
return(pass);
}
```
--------------------------------
### Basic ESI Include Tag Example
Source: https://github.com/dip-proto/fastly.js/blob/master/docs/tutorials/06-advanced-features.md
Demonstrates the basic ESI include tag used to dynamically insert content from specified URLs into an HTML page.
```html
My Page
This is the main content.
```
--------------------------------
### vcl_pass Example: Backend Selection for Non-Cached Requests
Source: https://github.com/dip-proto/fastly.js/blob/master/docs/reference/vcl-subroutines.md
Shows how to set the backend based on the request URL for non-cached requests in the vcl_pass subroutine, similar to vcl_miss.
```vcl
sub vcl_pass {
# Set the backend based on the request
if (req.url ~ "^/api/") {
set req.backend = api_backend;
} else if (req.url ~ "\.(jpg|jpeg|png|gif)$") {
set req.backend = image_backend;
}
# Pass the request to the backend
return(pass);
}
```
--------------------------------
### Select Backend using Director
Source: https://github.com/dip-proto/fastly.js/blob/master/docs/reference/vcl-variables.md
Dynamically select a backend server using a Fastly director and assign it to the request. This example uses a director named 'my_director'.
```vcl
set req.backend = std.director.select_backend("my_director").name;
```
--------------------------------
### Get Current Time
Source: https://github.com/dip-proto/fastly.js/blob/master/docs/api/standard-library.md
Retrieves the current time as a Unix timestamp. Use this to get a starting point for time calculations.
```typescript
const now = std.time.now();
```
--------------------------------
### Programmatic Header Manipulation
Source: https://github.com/dip-proto/fastly.js/blob/master/docs/api/http-object-model.md
Provides examples of using the standard library's header module in TypeScript for getting, setting, removing, and filtering headers.
```typescript
context.std.header.get(headers, "X-Token");
context.std.header.set(headers, "X-Token", "abc");
context.std.header.remove(headers, "X-Token");
context.std.header.filter(headers, "^X-Internal-");
context.std.header.filter_except(headers, "^Cache-");
```
--------------------------------
### Complete Content Negotiation System Example
Source: https://github.com/dip-proto/fastly.js/blob/master/fastly-vcl/vcl-functions/accept_header_functions.md
Demonstrates a comprehensive content negotiation system by combining language, media type, charset, and encoding lookups. It sets custom headers and routes requests to different backends based on client preferences. Includes setting Vary headers in vcl_deliver for proper caching.
```vcl
sub vcl_recv {
# Step 1: Determine the client's preferred language
declare local var.language STRING;
set var.language = accept.language_lookup(
"en:fr:de:ja:es",
"en",
req.http.Accept-Language
);
# Step 2: Determine the client's preferred media type
declare local var.format STRING;
set var.format = accept.media_lookup(
"application/json:application/xml:text/html",
"text/html",
"application/json:application/xml:text/html", # Media type patterns
req.http.Accept
);
# Step 3: Determine the client's preferred charset
declare local var.charset STRING;
set var.charset = accept.charset_lookup(
"utf-8:iso-8859-1",
"utf-8",
req.http.Accept-Charset
);
# Step 4: Determine the client's preferred encoding
declare local var.encoding STRING;
set var.encoding = accept.encoding_lookup(
"br:gzip:identity",
"identity",
req.http.Accept-Encoding
);
# Step 5: Set headers for backend to use
set req.http.X-Content-Language = var.language;
set req.http.X-Content-Type = var.format;
set req.http.X-Content-Charset = var.charset;
set req.http.X-Content-Encoding = var.encoding;
# Step 6: Implement content-based routing
# Route to different backends based on content negotiation results
if (var.format == "application/json") {
# JSON API backend
set req.backend = F_api_backend;
} else if (var.format == "text/html") {
# HTML website backend
if (var.language == "ja") {
# Japanese-specific backend
set req.backend = F_japan_website_backend;
} else {
# Default website backend
set req.backend = F_website_backend;
}
}
# Step 7: Set appropriate Vary headers in vcl_deliver
# This ensures proper caching based on content negotiation
}
sub vcl_deliver {
# Set appropriate Vary headers based on content negotiation
declare local var.vary_headers STRING;
set var.vary_headers = "";
if (resp.http.Content-Language) {
set var.vary_headers = "Accept-Language";
}
if (resp.http.Content-Type) {
if (var.vary_headers != "") {
set var.vary_headers = var.vary_headers + ", Accept";
} else {
set var.vary_headers = "Accept";
}
}
if (resp.http.Content-Encoding && resp.http.Content-Encoding != "identity") {
if (var.vary_headers != "") {
set var.vary_headers = var.vary_headers + ", Accept-Encoding";
} else {
set var.vary_headers = "Accept-Encoding";
}
}
if (var.vary_headers != "") {
set resp.http.Vary = var.vary_headers;
}
}
```
--------------------------------
### Conditional Backend Routing by URL
Source: https://github.com/dip-proto/fastly.js/blob/master/docs/reference/vcl-variables.md
Route requests to a specific backend based on URL patterns. This example directs requests starting with "/api/" to the 'api_backend'.
```vcl
if (req.url ~ "^/api/") {
set req.backend = api_backend;
}
```
--------------------------------
### Basic VCL Execution Example
Source: https://github.com/dip-proto/fastly.js/blob/master/docs/api/vcl-runtime.md
Demonstrates loading a VCL file, creating a context, setting request properties, and executing the `vcl_recv` subroutine. The returned action and modified context are then logged.
```typescript
import { createVCLContext, executeVCL } from "../src/vcl";
import { loadVCL } from "../src/node-loader";
const subroutines = loadVCL("./filter.vcl");
const context = createVCLContext();
context.req.method = "GET";
context.req.url = "/api/users";
context.req.http = {
host: "example.com",
"user-agent": "Mozilla/5.0",
};
const action = executeVCL(subroutines, "vcl_recv", context);
console.log(action); // e.g. "lookup", "pass", "error", "restart"
console.log(context.resp.status); // populated after vcl_deliver runs
console.log(context.resp.http); // response headers
```
--------------------------------
### Keep Parameters with Wildcard using globfilter_except
Source: https://github.com/dip-proto/fastly.js/blob/master/fastly-vcl/vcl-functions/query_string_functions.md
Shows how to use querystring.globfilter_except with a wildcard ('?') to keep parameters that match a partial pattern. The example filters for parameters starting with 'p' but not exactly 'p'.
```vcl
declare local var.result4 STRING;
set var.original_qs = "p=1&page=1&pg=1&id=123&debug=true";
set var.result4 = querystring.globfilter_except(var.original_qs, "p?");
# var.result4 is now "pg=1"
# Note: Only parameter "pg" is kept (matches "p?" pattern)
log "Result 4: " + var.result4;
```
--------------------------------
### Integrated Example: Complete Table-Based Configuration System
Source: https://github.com/dip-proto/fastly.js/blob/master/fastly-vcl/vcl-functions/table_functions.md
This VCL snippet demonstrates a comprehensive configuration system using various table functions. It dynamically sets environment, checks for maintenance mode with path and IP exemptions, applies feature flags, determines rate limits based on URL path, and configures cache TTLs based on content type.
```vcl
sub vcl_recv {
# Step 1: Determine the environment
declare local var.environment STRING;
declare local var.is_production BOOL;
# Get the environment from a header or hostname
if (req.http.X-Environment) {
set var.environment = req.http.X-Environment;
} else if (req.http.Host ~ "^prod\.") {
set var.environment = "production";
} else if (req.http.Host ~ "^stage\.") {
set var.environment = "staging";
} else if (req.http.Host ~ "^dev\.") {
set var.environment = "development";
} else {
set var.environment = "unknown";
}
# Check if this is the production environment
set var.is_production = (var.environment == "production");
# Step 2: Load environment-specific configuration
declare local var.config_prefix STRING;
# Set the configuration prefix for this environment
set var.config_prefix = var.environment + ":";
# Step 3: Check for maintenance mode
declare local var.maintenance_mode BOOL;
declare local var.maintenance_path_exempt BOOL;
# Check if maintenance mode is enabled for this environment
set var.maintenance_mode = table.lookup_bool(config, var.config_prefix + "maintenance_mode", false);
if (var.maintenance_mode) {
# Check if the current path is exempt from maintenance mode
set var.maintenance_path_exempt = false;
# Check against exempt paths
declare local var.exempt_paths_pattern REGEX;
set var.exempt_paths_pattern = table.lookup_regex(config, var.config_prefix + "maintenance_exempt_paths");
if (var.exempt_paths_pattern != "" && req.url.path ~ var.exempt_paths_pattern) {
set var.maintenance_path_exempt = true;
}
# Check if the client IP is exempt from maintenance mode
declare local var.maintenance_exempt_acl ACL;
set var.maintenance_exempt_acl = table.lookup_acl(acls, var.config_prefix + "maintenance_exempt");
if (!var.maintenance_path_exempt && !(client.ip ~ var.maintenance_exempt_acl)) {
# Show maintenance page
error 503 "Service Unavailable";
}
}
# Step 4: Apply feature flags
declare local var.feature_prefix STRING;
# Set the feature prefix for this environment
set var.feature_prefix = var.environment + ":feature:";
# Check and apply feature flags
declare local var.new_ui_enabled BOOL;
declare local var.new_api_enabled BOOL;
declare local var.new_checkout_enabled BOOL;
set var.new_ui_enabled = table.lookup_bool(features, var.feature_prefix + "new_ui", false);
set var.new_api_enabled = table.lookup_bool(features, var.feature_prefix + "new_api", false);
set var.new_checkout_enabled = table.lookup_bool(features, var.feature_prefix + "new_checkout", false);
# Set feature flags in headers
set req.http.X-Feature-New-UI = if(var.new_ui_enabled, "enabled", "disabled");
set req.http.X-Feature-New-API = if(var.new_api_enabled, "enabled", "disabled");
set req.http.X-Feature-New-Checkout = if(var.new_checkout_enabled, "enabled", "disabled");
# Step 5: Apply rate limits
declare local var.rate_limit_prefix STRING;
declare local var.rate_limit INTEGER;
# Set the rate limit prefix for this environment
set var.rate_limit_prefix = var.environment + ":rate_limit:";
# Get the appropriate rate limit based on the path
if (req.url.path ~ "^/api/") {
set var.rate_limit = table.lookup_integer(rate_limits, var.rate_limit_prefix + "api", 100);
} else if (req.url.path ~ "^/admin/") {
set var.rate_limit = table.lookup_integer(rate_limits, var.rate_limit_prefix + "admin", 50);
} else {
set var.rate_limit = table.lookup_integer(rate_limits, var.rate_limit_prefix + "default", 200);
}
# Set the rate limit in a header
set req.http.X-Rate-Limit = var.rate_limit;
# Step 6: Apply cache TTLs
declare local var.cache_ttl_prefix STRING;
declare local var.cache_ttl RTIME;
# Set the cache TTL prefix for this environment
set var.cache_ttl_prefix = var.environment + ":cache_ttl:";
# Get the appropriate cache TTL based on the content type
if (req.http.Content-Type) {
# Try to get a content-type specific TTL
set var.cache_ttl = table.lookup_rtime(cache_ttls, var.cache_ttl_prefix + req.http.Content-Type);
if (var.cache_ttl == 0s) {
# Fall back to default TTL
set var.cache_ttl = table.lookup_rtime(cache_ttls, var.cache_ttl_prefix + "default", 1h);
}
} else {
# Use default TTL
set var.cache_ttl = table.lookup_rtime(cache_ttls, var.cache_ttl_prefix + "default", 1h);
}
# Set the cache TTL in a header
set req.http.X-Cache-TTL = var.cache_ttl;
}
```
--------------------------------
### Complete Content Rewriting Example
Source: https://github.com/dip-proto/fastly.js/blob/master/docs/examples/content-rewriting.md
This VCL snippet demonstrates setting default backends, enabling ESI and gzip compression, and performing various body modifications like inserting banners, replacing domains, and adding footers. It also shows how to add custom headers.
```vcl
sub vcl_recv {
# Set the default backend
set req.backend = default;
return(lookup);
}
sub vcl_fetch {
# Enable ESI processing for HTML responses
if (beresp.http.Content-Type ~ "text/html") {
set beresp.do_esi = true;
}
# Enable gzip compression for text-based responses
if (beresp.http.Content-Type ~ "text|application/json|application/javascript") {
set beresp.do_gzip = true;
}
return(deliver);
}
sub vcl_deliver {
# Only process HTML responses
if (resp.http.Content-Type ~ "text/html") {
# Define the banner HTML
declare local var.banner STRING;
set var.banner = "Special offer: 20% off all items today!
";
# Insert the banner after the opening body tag
set resp.body = regsuball(resp.body, "", "" + var.banner);
# Replace all references to the old domain with the new domain
set resp.body = regsuball(resp.body, "old-domain.com", "new-domain.com");
# Add a custom footer
set resp.body = regsuball(resp.body, "", "");
}
# Add custom headers
set resp.http.X-Powered-By = "Fastly.JS";
return(deliver);
}
```
--------------------------------
### Complete Query String Management System Example
Source: https://github.com/dip-proto/fastly.js/blob/master/fastly-vcl/vcl-functions/query_string_functions.md
Demonstrates a comprehensive system for managing query strings, including extracting and normalizing parameters, cleaning, filtering, sorting, and setting values for caching and debugging.
```vcl
sub vcl_recv {
# Step 1: Extract and normalize essential parameters
# Get pagination parameters with defaults
declare local var.page STRING;
declare local var.limit STRING;
set var.page = querystring.get(req.url.qs, "page");
set var.limit = querystring.get(req.url.qs, "limit");
# Set default values if missing or invalid
if (var.page == "" || std.atoi(var.page) < 1) {
set var.page = "1";
}
if (var.limit == "" || std.atoi(var.limit) < 1 || std.atoi(var.limit) > 100) {
set var.limit = "20";
}
# Get sorting parameters
declare local var.sort_field STRING;
declare local var.sort_dir STRING;
set var.sort_field = querystring.get(req.url.qs, "sort");
set var.sort_dir = querystring.get(req.url.qs, "dir");
# Set default values if missing or invalid
if (var.sort_field == "") {
set var.sort_field = "date";
}
if (var.sort_dir == "" || (var.sort_dir != "asc" && var.sort_dir != "desc")) {
set var.sort_dir = "desc";
}
# Step 2: Clean the query string
declare local var.cleaned_qs STRING;
# Remove empty parameters
set var.cleaned_qs = querystring.clean(req.url.qs);
# Step 3: Remove tracking and debug parameters
declare local var.tracking_pattern STRING;
set var.tracking_pattern = "^utm_|^fb_|^ga_|^msclkid$" + "|^fbclid$|^gclid$|^dclid$|^debug$|^test$";
set var.cleaned_qs = querystring.filter(var.cleaned_qs, var.tracking_pattern);
# Step 4: Keep only essential parameters for caching
declare local var.cache_qs STRING;
declare local var.essential_pattern STRING;
set var.essential_pattern = "^(id|category|page|limit|sort|dir|q|filter)$";
set var.cache_qs = querystring.filter_except(var.cleaned_qs, var.essential_pattern);
# Step 5: Sort the parameters for consistent cache keys
set var.cache_qs = querystring.sort(var.cache_qs);
# Step 6: Set normalized parameters
declare local var.normalized_qs STRING;
set var.normalized_qs = var.cleaned_qs;
set var.normalized_qs = querystring.set(var.normalized_qs, "page", var.page);
set var.normalized_qs = querystring.set(var.normalized_qs, "limit", var.limit);
set var.normalized_qs = querystring.set(var.normalized_qs, "sort", var.sort_field);
set var.normalized_qs = querystring.set(var.normalized_qs, "dir", var.sort_dir);
# Step 7: Update the request URL with the normalized query string
if (var.normalized_qs == "") {
set req.url = req.url.path;
} else {
set req.url = req.url.path + "?" + var.normalized_qs;
}
# Step 8: Set cache key based on essential parameters
set req.http.X-Cache-Key = req.url.path + "?" + var.cache_qs;
# Step 9: Store original and normalized query strings for debugging
set req.http.X-Original-QueryString = req.url.qs;
set req.http.X-Normalized-QueryString = var.normalized_qs;
set req.http.X-Cache-QueryString = var.cache_qs;
}
```
--------------------------------
### Check if Maintenance Window Has Started
Source: https://github.com/dip-proto/fastly.js/blob/master/fastly-vcl/vcl-functions/time_functions.md
Determines if a scheduled maintenance period has begun by comparing the current time with the maintenance start time.
```vcl
declare local var.maintenance_start TIME;
declare local var.maintenance_started BOOL;
# Set maintenance start time to 1 hour from now
set var.maintenance_start = time.add(now, 1h);
# Check if maintenance has started
set var.maintenance_started = time.is_after(now, var.maintenance_start);
# Log the result
log "Has maintenance started? " + if(var.maintenance_started, "Yes", "No"); # Should be "No"
```
--------------------------------
### Build a Query String with querystring.add
Source: https://github.com/dip-proto/fastly.js/blob/master/fastly-vcl/vcl-functions/query_string_functions.md
Demonstrates how to construct a query string by adding parameters sequentially to an initially empty string using the `querystring.add` function.
```vcl
set var.built_qs = "";
set var.built_qs = querystring.add(var.built_qs, "product", "laptop");
set var.built_qs = querystring.add(var.built_qs, "brand", "acme");
set var.built_qs = querystring.add(var.built_qs, "price", "500-1000");
# var.built_qs is now "product=laptop&brand=acme&price=500-1000"
log "Built query string: " + var.built_qs;
```
--------------------------------
### Time-Based Routing
Source: https://github.com/dip-proto/fastly.js/blob/master/fastly-vcl/vcl-functions/table_functions.md
Looks up the start and end times for a route from the 'route_times' table. Activates the route if the current time falls within the specified start and end times.
```vcl
declare local var.route_name STRING;
declare local var.route_start_time TIME;
declare local var.route_end_time TIME;
set var.route_name = "holiday_special";
# Look up the start and end times for this route
set var.route_start_time = table.lookup_time(route_times, var.route_name + ":start");
set var.route_end_time = table.lookup_time(route_times, var.route_name + ":end");
# Check if the route should be active
if (var.route_start_time != 0s && var.route_end_time != 0s &&
now >= var.route_start_time && now <= var.route_end_time) {
set req.http.X-Route-Active = "true";
} else {
set req.http.X-Route-Active = "false";
}
```
--------------------------------
### Integrated Binary Data Processing System Example
Source: https://github.com/dip-proto/fastly.js/blob/master/fastly-vcl/vcl-functions/binary_data_functions.md
Demonstrates a comprehensive system using various binary data functions to extract, normalize, process, and prepare binary data for backend requests. It handles different input encodings and prepares output in formats suitable for different backends.
```vcl
sub vcl_recv {
# Step 1: Extract binary data from different sources
declare local var.raw_data STRING;
declare local var.encoding STRING;
# Determine the source and encoding of the data
if (req.http.Content-Type == "application/base64") {
set var.raw_data = req.http.X-Binary-Data;
set var.encoding = "base64";
} else if (req.http.Content-Type == "application/hex") {
set var.raw_data = req.http.X-Binary-Data;
set var.encoding = "hex";
} else {
# Default to UTF-8 text
set var.raw_data = req.http.X-Binary-Data;
set var.encoding = "utf8";
}
# Step 2: Normalize to a common format (hex) for processing
declare local var.normalized_hex STRING;
if (var.encoding == "base64") {
set var.normalized_hex = bin.base64_to_hex(var.raw_data);
} else if (var.encoding == "hex") {
set var.normalized_hex = var.raw_data;
} else {
# Convert from UTF-8 to hex
set var.normalized_hex = bin.data_convert(var.raw_data, "utf8", "hex");
}
# Step 3: Process the data in hex format
# This could involve extracting fields, validating checksums, etc.
declare local var.data_valid BOOL;
declare local var.data_type STRING;
declare local var.data_length INTEGER;
# Example: Extract header information from a simple binary format
# Format: [1 byte type][2 bytes length][variable data]
if (std.strlen(var.normalized_hex) >= 6) {
# Extract type (first byte)
set var.data_type = substr(var.normalized_hex, 0, 2);
# Extract length (next 2 bytes)
set var.data_length = std.strtol(substr(var.normalized_hex, 2, 4), 16);
# Validate the data
if (std.strlen(var.normalized_hex) >= (6 + var.data_length * 2)) {
set var.data_valid = true;
} else {
set var.data_valid = false;
}
} else {
set var.data_valid = false;
}
# Step 4: Take action based on the processed data
if (var.data_valid) {
if (var.data_type == "01") {
# Type 0x01: Authentication token
# Extract the token data
declare local var.token_hex STRING;
set var.token_hex = substr(var.normalized_hex, 6, var.data_length * 2);
# Convert to base64 for use in Authorization header
declare local var.token_base64 STRING;
set var.token_base64 = bin.hex_to_base64(var.token_hex);
# Set the Authorization header
set req.http.Authorization = "Bearer " + var.token_base64;
} else if (var.data_type == "02") {
# Type 0x02: Encrypted payload
# Extract the encrypted data
declare local var.encrypted_hex STRING;
set var.encrypted_hex = substr(var.normalized_hex, 6, var.data_length * 2);
# In a real scenario, you might decrypt this data
# For this example, we'll just pass it along
set req.http.X-Encrypted-Data = var.encrypted_hex;
} else {
# Unknown type
set req.http.X-Unknown-Data-Type = var.data_type;
}
} else {
# Invalid data
set req.http.X-Data-Error = "Invalid binary data format";
}
# Step 5: Prepare data for the backend in the required format
declare local var.backend_format STRING;
declare local var.backend_data STRING;
# Determine the format required by the backend
if (req.backend == F_json_backend) {
set var.backend_format = "utf8"; # JSON backend expects UTF-8
} else if (req.backend == F_binary_backend) {
set var.backend_format = "base64"; # Binary backend expects base64
} else {
set var.backend_format = "hex"; # Default to hex
}
# Convert the normalized hex data to the required format
set var.backend_data = bin.data_convert(var.normalized_hex, "hex", var.backend_format);
# Set the appropriate header for the backend
set req.http.X-Processed-Data = var.backend_data;
}
```
--------------------------------
### Generate CSRF Token for GET Requests
Source: https://github.com/dip-proto/fastly.js/blob/master/docs/csrf-protection.md
Generates a SHA256 hash for a CSRF token in VCL's vcl_recv subroutine for GET requests. Requires client IP, User-Agent, a secret salt, and the current time.
```vcl
sub vcl_recv {
# For GET requests, generate a CSRF token
if (req.method == "GET") {
set req.http.X-CSRF-Token = digest.hash_sha256(
client.ip +
req.http.User-Agent +
"secret-salt" +
std.time.hex_to_time(time.hex)
);
}
# Continue processing the request
return(lookup);
}
```
--------------------------------
### Fastly.js AB Testing VCL Example
Source: https://github.com/dip-proto/fastly.js/blob/master/docs/examples/ab-testing.md
This is the main VCL code for the AB testing example. It handles variant assignment in vcl_recv, cache key generation in vcl_hash, response modification in vcl_fetch, cookie setting in vcl_deliver, and logging.
```vcl
sub vcl_recv {
# Check if the user already has an A/B test variant cookie
if (!req.http.X-ABTest || req.http.X-ABTest-Reset) {
# Assign a new variant: 40% A, 40% B, 20% C
if (std.random.randombool(0.4)) {
set req.http.X-ABTest = "A";
std.log("Assigning user to variant A");
} elsif (std.random.randombool(0.5)) { # 0.5 of the remaining 60% is 30%, so 40% + 30% = 70%
set req.http.X-ABTest = "B";
std.log("Assigning user to variant B");
} else {
set req.http.X-ABTest = "C";
std.log("Assigning user to variant C");
}
# Set a flag to add the cookie in vcl_deliver
set req.http.X-Set-ABTest-Cookie = "1";
}
# Pass the request to the next subroutine
return(pass);
}
sub vcl_hash {
# Create a cache key based on the URL, host, and A/B test variant
return(hash);
}
sub vcl_fetch {
# Add the A/B test variant to the response
set req.http.X-ABTest = req.http.X-ABTest;
# Modify the response based on the A/B test variant
if (req.http.X-ABTest == "B") {
# Variant B: Modified version (example: change the title)
set req.http.X-AB-Modified = "1";
# Example modification: change the title
# In a real scenario, you would modify the actual content here.
# For this example, we'll just add a header to indicate modification.
} elsif (req.http.X-ABTest == "C") {
# Variant C: Another modified version (example: change the layout)
set req.http.X-AB-Modified = "1";
# Example modification: change the layout
# In a real scenario, you would modify the actual content here.
# For this example, we'll just add a header to indicate modification.
}
# Pass the request to the next subroutine
return(pass);
}
sub vcl_deliver {
# Set the A/B test cookie if needed
if (req.http.X-Set-ABTest-Cookie == "1") {
set resp.http.Set-Cookie = "ABTest="+req.http.X-ABTest+"; Max-Age=2592000; Path=/;";
}
# Add debugging headers
set resp.http.X-ABTest = req.http.X-ABTest;
if (req.http.X-AB-Modified == "1") {
set resp.http.X-AB-Modified = "true";
}
# Pass the request to the next subroutine
return(pass);
}
sub vcl_log {
# Log the completed request with the A/B test variant
std.log("ABTest Variant: " + req.http.X-ABTest);
return(ok);
}
```
--------------------------------
### VCL Subroutine Declaration Example
Source: https://github.com/dip-proto/fastly.js/blob/master/docs/api/vcl-compiler.md
Illustrates a VCL subroutine declaration for `vcl_recv`.
```vcl
sub vcl_recv {
set req.http.X-Test = "Hello, World!";
return(lookup);
}
```
--------------------------------
### VCL Comments
Source: https://github.com/dip-proto/fastly.js/blob/master/fastly-vcl/03-vcl-basics.md
VCL supports single-line comments starting with // and multi-line comments enclosed in /* */.
```vcl
// This is a single-line comment
/*
This is a
multi-line comment
*/
```