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 <mholt@users.noreply.github.com>
This commit is contained in:
Isaac Parker 2022-09-15 12:35:38 -06:00 committed by GitHub
parent 9ad0ebc956
commit e338648fed
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23

View file

@ -41,6 +41,7 @@ A simple but production-ready reverse proxy. Useful for quick deployments,
demos, and development. demos, and development.
Simply shuttles HTTP(S) traffic from the --from address to the --to address. 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 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 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 { Flags: func() *flag.FlagSet {
fs := flag.NewFlagSet("reverse-proxy", flag.ExitOnError) fs := flag.NewFlagSet("reverse-proxy", flag.ExitOnError)
fs.String("from", "localhost", "Address on which to receive traffic") 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("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("insecure", false, "Disable TLS verification (WARNING: DISABLES SECURITY BY NOT VERIFYING SSL CERTIFICATES!)")
fs.Bool("internal-certs", false, "Use internal CA for issuing certs") fs.Bool("internal-certs", false, "Use internal CA for issuing certs")
@ -70,7 +71,6 @@ func cmdReverseProxy(fs caddycmd.Flags) (int, error) {
caddy.TrapSignals() caddy.TrapSignals()
from := fs.String("from") from := fs.String("from")
to := fs.String("to")
changeHost := fs.Bool("change-host-header") changeHost := fs.Bool("change-host-header")
insecure := fs.Bool("insecure") insecure := fs.Bool("insecure")
internalCerts := fs.Bool("internal-certs") internalCerts := fs.Bool("internal-certs")
@ -78,7 +78,7 @@ func cmdReverseProxy(fs caddycmd.Flags) (int, error) {
httpPort := strconv.Itoa(caddyhttp.DefaultHTTPPort) httpPort := strconv.Itoa(caddyhttp.DefaultHTTPPort)
httpsPort := strconv.Itoa(caddyhttp.DefaultHTTPSPort) httpsPort := strconv.Itoa(caddyhttp.DefaultHTTPSPort)
if to == "" { if len(reverseProxyCmdTo) == 0 {
return caddy.ExitCodeFailedStartup, fmt.Errorf("--to is required") 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 // set up the upstream address; assume missing information from given parts
toAddr, toScheme, err := parseUpstreamDialAddress(to) // mixing schemes isn't supported, so use first defined (if available)
if err != nil { toAddresses := make([]string, len(reverseProxyCmdTo))
return caddy.ExitCodeFailedStartup, fmt.Errorf("invalid upstream address %s: %v", to, err) 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 // 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{ handler := Handler{
TransportRaw: caddyconfig.JSONModuleObject(ht, "protocol", "http", nil), TransportRaw: caddyconfig.JSONModuleObject(ht, "protocol", "http", nil),
Upstreams: UpstreamPool{{Dial: toAddr}}, Upstreams: upstreamPool,
} }
if changeHost { if changeHost {
@ -187,7 +203,15 @@ func cmdReverseProxy(fs caddycmd.Flags) (int, error) {
return caddy.ExitCodeFailedStartup, err 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 {} select {}
} }
// reverseProxyCmdTo holds the parsed values from repeated use of the --to flag.
var reverseProxyCmdTo caddycmd.StringSlice