From 68052392e66cabb7865ca1fbf228daf973b34193 Mon Sep 17 00:00:00 2001 From: Hubert Grochowski Date: Thu, 18 Jan 2024 15:49:58 +0100 Subject: [PATCH] http_{server,proxy}: support multiple ports binding Fixes #629 --- bind/flag.go | 5 +++-- command/run/run.go | 7 ++++-- command/test/httpbin/httpbin.go | 5 ++++- http_proxy.go | 13 ++++++++--- http_server.go | 38 ++++++++++++++++++++++++++++----- 5 files changed, 55 insertions(+), 13 deletions(-) diff --git a/bind/flag.go b/bind/flag.go index 239d3550a..d5cb84543 100644 --- a/bind/flag.go +++ b/bind/flag.go @@ -239,9 +239,10 @@ func HTTPServerConfig(fs *pflag.FlagSet, cfg *forwarder.HTTPServerConfig, prefix namePrefix += "-" } - fs.StringVarP(&cfg.Addr, - namePrefix+"address", "", cfg.Addr, ""+ + fs.VarP(anyflag.NewValue[forwarder.MultiplePortsAddr](cfg.Addr, &cfg.Addr, forwarder.ParseMultiplePortsAddr), + namePrefix+"address", "", "[host]:"+ "The server address to listen on. "+ + "Multiple ports can be specified separated by commas. "+ "If the host is empty, the server will listen on all available interfaces. ") if schemes == nil { diff --git a/command/run/run.go b/command/run/run.go index e7245394d..b37fe8ff3 100644 --- a/command/run/run.go +++ b/command/run/run.go @@ -224,7 +224,7 @@ func (c *command) runE(cmd *cobra.Command, _ []string) (cmdErr error) { } } - if c.apiServerConfig.Addr != "" { + if !c.apiServerConfig.Addr.IsZero() { if err := c.registerProcMetrics(); err != nil { return fmt.Errorf("register process metrics: %w", err) } @@ -317,7 +317,10 @@ func Command() *cobra.Command { } c.httpProxyConfig.PromRegistry = c.promReg c.httpProxyConfig.PromNamespace = promNs - c.apiServerConfig.Addr = "localhost:10000" + c.apiServerConfig.Addr = forwarder.MultiplePortsAddr{ + Host: "localhost", + Ports: []string{"10000"}, + } cmd := &cobra.Command{ Use: "run [--address ] [--pac ] [--credentials ]...", diff --git a/command/test/httpbin/httpbin.go b/command/test/httpbin/httpbin.go index 967e67485..f44ee8f33 100644 --- a/command/test/httpbin/httpbin.go +++ b/command/test/httpbin/httpbin.go @@ -84,7 +84,10 @@ func Command() *cobra.Command { apiServerConfig: forwarder.DefaultHTTPServerConfig(), logConfig: log.DefaultConfig(), } - c.apiServerConfig.Addr = "localhost:10000" + c.apiServerConfig.Addr = forwarder.MultiplePortsAddr{ + Host: "localhost", + Ports: []string{"10000"}, + } cmd := &cobra.Command{ Use: "httpbin [--protocol ] [--address ] [flags]", diff --git a/http_proxy.go b/http_proxy.go index f723de604..173a499c0 100644 --- a/http_proxy.go +++ b/http_proxy.go @@ -105,8 +105,10 @@ type HTTPProxyConfig struct { func DefaultHTTPProxyConfig() *HTTPProxyConfig { return &HTTPProxyConfig{ HTTPServerConfig: HTTPServerConfig{ - Protocol: HTTPScheme, - Addr: ":3128", + Protocol: HTTPScheme, + Addr: MultiplePortsAddr{ + Ports: []string{"3128"}, + }, ReadHeaderTimeout: 1 * time.Minute, TLSServerConfig: TLSServerConfig{ HandshakeTimeout: 10 * time.Second, @@ -609,8 +611,13 @@ func (hp *HTTPProxy) listen() (net.Listener, error) { return nil, fmt.Errorf("invalid protocol %q", hp.config.Protocol) } + addrs := make([]string, 0, len(hp.config.Addr.Ports)) + for _, port := range hp.config.Addr.Ports { + addrs = append(addrs, net.JoinHostPort(hp.config.Addr.Host, port)) + } + l := Listener{ - Addresses: []string{hp.config.Addr}, + Addresses: addrs, Log: hp.log, TLSConfig: hp.tlsConfig, TLSHandshakeTimeout: hp.config.TLSServerConfig.HandshakeTimeout, diff --git a/http_server.go b/http_server.go index 19efada6e..220d7a43a 100644 --- a/http_server.go +++ b/http_server.go @@ -14,6 +14,7 @@ import ( "net" "net/http" "net/url" + "strings" "sync" "time" @@ -73,9 +74,30 @@ func h2TLSConfigTemplate() *tls.Config { } } +type MultiplePortsAddr struct { + Host string + Ports []string +} + +func ParseMultiplePortsAddr(val string) (MultiplePortsAddr, error) { + i := strings.LastIndex(val, ":") + if i == -1 || i == len(val)-1 { + return MultiplePortsAddr{}, fmt.Errorf("invalid address %q", val) + } + + return MultiplePortsAddr{ + Host: val[:i], + Ports: strings.Split(val[i+1:], ","), + }, nil +} + +func (m MultiplePortsAddr) IsZero() bool { + return m.Host == "" && len(m.Ports) == 0 +} + type HTTPServerConfig struct { Protocol Scheme - Addr string + Addr MultiplePortsAddr TLSServerConfig IdleTimeout time.Duration ReadTimeout time.Duration @@ -90,8 +112,10 @@ type HTTPServerConfig struct { func DefaultHTTPServerConfig() *HTTPServerConfig { return &HTTPServerConfig{ - Protocol: HTTPScheme, - Addr: ":8080", + Protocol: HTTPScheme, + Addr: MultiplePortsAddr{ + Ports: []string{"8080"}, + }, ReadHeaderTimeout: 1 * time.Minute, } } @@ -121,7 +145,6 @@ func NewHTTPServer(cfg *HTTPServerConfig, h http.Handler, log log.Logger) (*HTTP config: *cfg, log: log, srv: &http.Server{ - Addr: cfg.Addr, Handler: withMiddleware(cfg, log, h), IdleTimeout: cfg.IdleTimeout, ReadTimeout: cfg.ReadTimeout, @@ -230,8 +253,13 @@ func (hs *HTTPServer) Run(ctx context.Context) error { } func (hs *HTTPServer) listen() (net.Listener, error) { + addrs := make([]string, 0, len(hs.config.Addr.Ports)) + for _, port := range hs.config.Addr.Ports { + addrs = append(addrs, net.JoinHostPort(hs.config.Addr.Host, port)) + } + l := Listener{ - Addresses: []string{hs.config.Addr}, + Addresses: addrs, Log: hs.log, TLSConfig: hs.srv.TLSConfig, TLSHandshakeTimeout: hs.config.HandshakeTimeout,