### 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 = ""; # 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 */ ```