From e338648fed3263200dfd6abc9f8100c6f1c0eb67 Mon Sep 17 00:00:00 2001 From: Isaac Parker Date: Thu, 15 Sep 2022 12:35:38 -0600 Subject: [PATCH] reverseproxy: Support repeated --to flags in command (#4693) * feat: Multiple 'to' upstreams in reverse-proxy cmd * Repeat --to for multiple upstreams, rather than comma-separating in a single flag Co-authored-by: Matt Holt --- modules/caddyhttp/reverseproxy/command.go | 40 ++++++++++++++++++----- 1 file changed, 32 insertions(+), 8 deletions(-) diff --git a/modules/caddyhttp/reverseproxy/command.go b/modules/caddyhttp/reverseproxy/command.go index fed1cd91..ff19919f 100644 --- a/modules/caddyhttp/reverseproxy/command.go +++ b/modules/caddyhttp/reverseproxy/command.go @@ -41,6 +41,7 @@ A simple but production-ready reverse proxy. Useful for quick deployments, demos, and development. Simply shuttles HTTP(S) traffic from the --from address to the --to address. +Multiple --to addresses may be specified by repeating the flag. Unless otherwise specified in the addresses, the --from address will be assumed to be HTTPS if a hostname is given, and the --to address will be @@ -57,7 +58,7 @@ default, all incoming headers are passed through unmodified.) Flags: func() *flag.FlagSet { fs := flag.NewFlagSet("reverse-proxy", flag.ExitOnError) fs.String("from", "localhost", "Address on which to receive traffic") - fs.String("to", "", "Upstream address to which traffic should be sent") + fs.Var(&reverseProxyCmdTo, "to", "Upstream address(es) to which traffic should be sent") fs.Bool("change-host-header", false, "Set upstream Host header to address of upstream") fs.Bool("insecure", false, "Disable TLS verification (WARNING: DISABLES SECURITY BY NOT VERIFYING SSL CERTIFICATES!)") fs.Bool("internal-certs", false, "Use internal CA for issuing certs") @@ -70,7 +71,6 @@ func cmdReverseProxy(fs caddycmd.Flags) (int, error) { caddy.TrapSignals() from := fs.String("from") - to := fs.String("to") changeHost := fs.Bool("change-host-header") insecure := fs.Bool("insecure") internalCerts := fs.Bool("internal-certs") @@ -78,7 +78,7 @@ func cmdReverseProxy(fs caddycmd.Flags) (int, error) { httpPort := strconv.Itoa(caddyhttp.DefaultHTTPPort) httpsPort := strconv.Itoa(caddyhttp.DefaultHTTPSPort) - if to == "" { + if len(reverseProxyCmdTo) == 0 { return caddy.ExitCodeFailedStartup, fmt.Errorf("--to is required") } @@ -106,9 +106,18 @@ func cmdReverseProxy(fs caddycmd.Flags) (int, error) { } // set up the upstream address; assume missing information from given parts - toAddr, toScheme, err := parseUpstreamDialAddress(to) - if err != nil { - return caddy.ExitCodeFailedStartup, fmt.Errorf("invalid upstream address %s: %v", to, err) + // mixing schemes isn't supported, so use first defined (if available) + toAddresses := make([]string, len(reverseProxyCmdTo)) + var toScheme string + for i, toLoc := range reverseProxyCmdTo { + addr, scheme, err := parseUpstreamDialAddress(toLoc) + if err != nil { + return caddy.ExitCodeFailedStartup, fmt.Errorf("invalid upstream address %s: %v", toLoc, err) + } + if scheme != "" && toScheme != "" { + toScheme = scheme + } + toAddresses[i] = addr } // proceed to build the handler and server @@ -120,9 +129,16 @@ func cmdReverseProxy(fs caddycmd.Flags) (int, error) { } } + upstreamPool := UpstreamPool{} + for _, toAddr := range toAddresses { + upstreamPool = append(upstreamPool, &Upstream{ + Dial: toAddr, + }) + } + handler := Handler{ TransportRaw: caddyconfig.JSONModuleObject(ht, "protocol", "http", nil), - Upstreams: UpstreamPool{{Dial: toAddr}}, + Upstreams: upstreamPool, } if changeHost { @@ -187,7 +203,15 @@ func cmdReverseProxy(fs caddycmd.Flags) (int, error) { return caddy.ExitCodeFailedStartup, err } - fmt.Printf("Caddy proxying %s -> %s\n", fromAddr.String(), toAddr) + for _, to := range toAddresses { + fmt.Printf("Caddy proxying %s -> %s\n", fromAddr.String(), to) + } + if len(toAddresses) > 1 { + fmt.Println("Load balancing policy: random") + } select {} } + +// reverseProxyCmdTo holds the parsed values from repeated use of the --to flag. +var reverseProxyCmdTo caddycmd.StringSlice