From 5e20efe0fdcde7b7fd640bd1475f65ad8881e63a Mon Sep 17 00:00:00 2001 From: Steffen Busch <37350514+steffenbusch@users.noreply.github.com> Date: Sat, 26 Oct 2024 21:02:09 +0200 Subject: [PATCH] Make integer rand min and max configurable (#5) * Add min rand_int_min and rand_int_max + rename placeholder * Add Validate and Provision w/ error handling * Adjust readme --- README.md | 68 +++++++++++++++++++++++++++++-------------- extra_placeholders.go | 57 +++++++++++++++++++++++++++++++----- 2 files changed, 96 insertions(+), 29 deletions(-) diff --git a/README.md b/README.md index 554d646..50d8685 100644 --- a/README.md +++ b/README.md @@ -6,16 +6,16 @@ This repository contains the **Extra Placeholders** plugin for the [Caddy](https This plugin introduces new placeholders that can be used within Caddy configurations: -| Placeholder | Description | -|--------------------------------|---------------------------------------------------| -| `{extra.caddy.version.simple}` | Simple version information of the Caddy server. | -| `{extra.caddy.version.full}` | Full version information of the Caddy server. | -| `{extra.rand.float}` | Random float value between 0.0 and 1.0. | -| `{extra.rand.int.0-100}` | Random integer value between 0 and 100. | -| `{extra.loadavg.1}` | System load average over the last 1 minute. | -| `{extra.loadavg.5}` | System load average over the last 5 minutes. | -| `{extra.loadavg.15}` | System load average over the last 15 minutes. | -| `{extra.hostinfo.uptime}` | System uptime in a human-readable format. | +| Placeholder | Description | +|--------------------------------|-------------------------------------------------------| +| `{extra.caddy.version.simple}` | Simple version information of the Caddy server. | +| `{extra.caddy.version.full}` | Full version information of the Caddy server. | +| `{extra.rand.float}` | Random float value between 0.0 and 1.0. | +| `{extra.rand.int}` | Random integer value between the configured min and max (default is 0 to 100). | +| `{extra.loadavg.1}` | System load average over the last 1 minute. | +| `{extra.loadavg.5}` | System load average over the last 5 minutes. | +| `{extra.loadavg.15}` | System load average over the last 15 minutes. | +| `{extra.hostinfo.uptime}` | System uptime in a human-readable format. | These placeholders can be used in Caddyfiles to provide dynamic content and system information in responses. @@ -36,13 +36,35 @@ To use the extra placeholders, you can add the following directive to your Caddy ```caddyfile :8080 { - extra_placeholders + extra_placeholders { + rand_int 10 50 + } - respond "Caddy Version: {extra.caddy.version.full}, Uptime: {extra.hostinfo.uptime}" + respond "Caddy Version: {extra.caddy.version.full}, Uptime: {extra.hostinfo.uptime}, Random Int: {extra.rand.int}" } ``` -This example demonstrates how to use the additional placeholders provided by this plugin to dynamically insert Caddy version information and system uptime into an HTTP response. +This example demonstrates how to use the additional placeholders provided by this plugin to dynamically insert Caddy version information, system uptime, and a random integer between 10 and 50 into an HTTP response. + +### Random Integer Configuration + +To configure the range for the `{extra.rand.int}` placeholder, use the `rand_int` subdirective inside the `extra_placeholders` directive. The format is: + +```caddyfile +extra_placeholders { + rand_int +} +``` + +- ``: The minimum value for the random integer (inclusive). +- ``: The maximum value for the random integer (inclusive). + +If `rand_int` is not specified, the default values are: + +- `RandIntMin = 0` +- `RandIntMax = 100` + +This means that `{extra.rand.int}` will default to generating a random integer between 0 and 100 if not explicitly configured. ### Example: Conditional Redirect Based on Random Value @@ -50,28 +72,30 @@ The following example demonstrates how you can use conditional expressions with ```caddyfile :8080 { - extra_placeholders + extra_placeholders { + rand_int 1 100 + } - @redirectToGoogle expression `{extra.rand.int.0-100} <= 25` + @redirectToGoogle expression `{extra.rand.int} <= 25` redir @redirectToGoogle https://www.google.com - @redirectToBing expression `{extra.rand.int.0-100} > 25 && {extra.rand.int.0-100} <= 50` + @redirectToBing expression `{extra.rand.int} > 25 && {extra.rand.int} <= 50` redir @redirectToBing https://www.bing.com - @redirectToYahoo expression `{extra.rand.int.0-100} > 50 && {extra.rand.int.0-100} <= 75` + @redirectToYahoo expression `{extra.rand.int} > 50 && {extra.rand.int} <= 75` redir @redirectToYahoo https://www.yahoo.com - @redirectToDuckDuckGo expression `{extra.rand.int.0-100} > 75` + @redirectToDuckDuckGo expression `{extra.rand.int} > 75` redir @redirectToDuckDuckGo https://www.duckduckgo.com } ``` In this example: -- If `{extra.rand.int.0-100}` is between 0 and 25, the request is redirected to **Google** ([https://www.google.com](https://www.google.com)). -- If `{extra.rand.int.0-100}` is between 26 and 50, the request is redirected to **Bing** ([https://www.bing.com](https://www.bing.com)). -- If `{extra.rand.int.0-100}` is between 51 and 75, the request is redirected to **Yahoo** ([https://www.yahoo.com](https://www.yahoo.com)). -- If `{extra.rand.int.0-100}` is greater than 75, the request is redirected to **DuckDuckGo** ([https://www.duckduckgo.com](https://www.duckduckgo.com)). +- If `{extra.rand.int}` is between 1 and 25, the request is redirected to **Google** ([https://www.google.com](https://www.google.com)). +- If `{extra.rand.int}` is between 26 and 50, the request is redirected to **Bing** ([https://www.bing.com](https://www.bing.com)). +- If `{extra.rand.int}` is between 51 and 75, the request is redirected to **Yahoo** ([https://www.yahoo.com](https://www.yahoo.com)). +- If `{extra.rand.int}` is greater than 75, the request is redirected to **DuckDuckGo** ([https://www.duckduckgo.com](https://www.duckduckgo.com)). This example demonstrates how to use the random integer placeholder in combination with conditional expressions to create dynamic redirection rules. diff --git a/extra_placeholders.go b/extra_placeholders.go index d481365..43e8339 100644 --- a/extra_placeholders.go +++ b/extra_placeholders.go @@ -15,8 +15,10 @@ package extraplaceholders import ( + "fmt" "math/rand" "net/http" + "strconv" "time" "github.com/caddyserver/caddy/v2" @@ -28,7 +30,10 @@ import ( ) // ExtraPlaceholders represents the structure for the plugin. -type ExtraPlaceholders struct{} +type ExtraPlaceholders struct { + RandIntMin int `json:"rand_int_min,omitempty"` + RandIntMax int `json:"rand_int_max,omitempty"` +} func init() { // Register the module with Caddy and specify where in the directive order it should be applied. @@ -42,7 +47,7 @@ func init() { // `{extra.caddy.version.simple}` | Simple version information of the Caddy server. // `{extra.caddy.version.full}` | Full version information of the Caddy server. // `{extra.rand.float}` | Random float value between 0.0 and 1.0. -// `{extra.rand.int.0-100}` | Random integer value between 0 and 100. +// `{extra.rand.int}` | Random integer value between the configured min and max (default is 0 to 100). // `{extra.loadavg.1}` | System load average over the last 1 minute. // `{extra.loadavg.5}` | System load average over the last 5 minutes. // `{extra.loadavg.15}` | System load average over the last 15 minutes. @@ -56,8 +61,26 @@ func (ExtraPlaceholders) CaddyModule() caddy.ModuleInfo { } } +// Provision sets up the module. It is called once the module is instantiated. +func (e *ExtraPlaceholders) Provision(ctx caddy.Context) error { + // Set default values if not configured + if e.RandIntMin == 0 && e.RandIntMax == 0 { + e.RandIntMin = 0 + e.RandIntMax = 100 + } + return nil +} + +// Validate ensures the configuration is correct. +func (e *ExtraPlaceholders) Validate() error { + if e.RandIntMax <= e.RandIntMin { + return fmt.Errorf("invalid configuration: RandIntMax (%d) must be greater than RandIntMin (%d)", e.RandIntMax, e.RandIntMin) + } + return nil +} + // ServeHTTP adds new placeholders and passes the request to the next handler in the chain. -func (ExtraPlaceholders) ServeHTTP(w http.ResponseWriter, r *http.Request, next caddyhttp.Handler) error { +func (e ExtraPlaceholders) ServeHTTP(w http.ResponseWriter, r *http.Request, next caddyhttp.Handler) error { // Retrieve the replacer from the request context. repl, ok := r.Context().Value(caddy.ReplacerCtxKey).(*caddy.Replacer) if !ok { @@ -71,7 +94,11 @@ func (ExtraPlaceholders) ServeHTTP(w http.ResponseWriter, r *http.Request, next // Set placeholders for random float and integer values. repl.Set("extra.rand.float", rand.Float64()) - repl.Set("extra.rand.int.0-100", rand.Intn(101)) + if e.RandIntMax > e.RandIntMin { + repl.Set("extra.rand.int", rand.Intn(e.RandIntMax-e.RandIntMin+1)+e.RandIntMin) + } else { + repl.Set("extra.rand.int", rand.Intn(101)) // Default range 0-100 if not properly configured + } // Set placeholders for system load averages (1, 5, and 15 minutes). loadAvg, err := load.Avg() @@ -103,10 +130,24 @@ func parseCaddyfile(h httpcaddyfile.Helper) (caddyhttp.MiddlewareHandler, error) // UnmarshalCaddyfile processes the configuration from the Caddyfile. func (e *ExtraPlaceholders) UnmarshalCaddyfile(d *caddyfile.Dispenser) error { - // Consume the directive name and ensure no extra arguments are provided. + // Consume the directive name. d.Next() - if d.NextArg() { - return d.ArgErr() + + for d.NextBlock(0) { + switch d.Val() { + case "rand_int": + args := d.RemainingArgs() + if len(args) != 2 { + return d.ArgErr() + } + min, err1 := strconv.Atoi(args[0]) + max, err2 := strconv.Atoi(args[1]) + if err1 != nil || err2 != nil { + return d.ArgErr() + } + e.RandIntMin = min + e.RandIntMax = max + } } return nil } @@ -114,5 +155,7 @@ func (e *ExtraPlaceholders) UnmarshalCaddyfile(d *caddyfile.Dispenser) error { // Interface guards to ensure ExtraPlaceholders implements the necessary interfaces. var ( _ caddy.Module = (*ExtraPlaceholders)(nil) + _ caddy.Provisioner = (*ExtraPlaceholders)(nil) + _ caddy.Validator = (*ExtraPlaceholders)(nil) _ caddyhttp.MiddlewareHandler = (*ExtraPlaceholders)(nil) )