From ae8a867924b4ee70c1f2cfb56425bd48300cfe86 Mon Sep 17 00:00:00 2001 From: Aliaksandr Valialkin Date: Fri, 9 Feb 2024 03:15:04 +0200 Subject: [PATCH] all: add support for specifying multiple -httpListenAddr options --- app/victoria-logs/main.go | 16 ++++-- app/victoria-metrics/main.go | 16 ++++-- app/victoria-metrics/main_test.go | 4 +- app/vmagent/main.go | 24 ++++---- app/vmalert-tool/unittest/unittest.go | 6 +- app/vmalert/main.go | 24 +++++--- app/vmauth/main.go | 16 ++++-- app/vmbackup/main.go | 7 ++- app/vmrestore/main.go | 7 ++- docs/CHANGELOG.md | 1 + lib/flagutil/array.go | 4 +- lib/flagutil/array_test.go | 12 ++-- lib/flagutil/dict_test.go | 3 - lib/httpserver/httpserver.go | 81 ++++++++++++++++++++------- 14 files changed, 141 insertions(+), 80 deletions(-) diff --git a/app/victoria-logs/main.go b/app/victoria-logs/main.go index 0a56dd91f..1a803e44b 100644 --- a/app/victoria-logs/main.go +++ b/app/victoria-logs/main.go @@ -21,8 +21,8 @@ import ( ) var ( - httpListenAddr = flag.String("httpListenAddr", ":9428", "TCP address to listen for http connections. See also -httpListenAddr.useProxyProtocol") - useProxyProtocol = flag.Bool("httpListenAddr.useProxyProtocol", false, "Whether to use proxy protocol for connections accepted at -httpListenAddr . "+ + httpListenAddrs = flagutil.NewArrayString("httpListenAddr", "TCP address to listen for incoming http requests. See also -httpListenAddr.useProxyProtocol") + useProxyProtocol = flagutil.NewArrayBool("httpListenAddr.useProxyProtocol", "Whether to use proxy protocol for connections accepted at the given -httpListenAddr . "+ "See https://www.haproxy.org/download/1.8/doc/proxy-protocol.txt . "+ "With enabled proxy protocol http server cannot serve regular /metrics endpoint. Use -pushmetrics.url for metrics pushing") ) @@ -35,14 +35,18 @@ func main() { buildinfo.Init() logger.Init() - logger.Infof("starting VictoriaLogs at %q...", *httpListenAddr) + listenAddrs := *httpListenAddrs + if len(listenAddrs) == 0 { + listenAddrs = []string{":9428"} + } + logger.Infof("starting VictoriaLogs at %q...", listenAddrs) startTime := time.Now() vlstorage.Init() vlselect.Init() vlinsert.Init() - go httpserver.Serve(*httpListenAddr, *useProxyProtocol, requestHandler) + go httpserver.Serve(listenAddrs, useProxyProtocol, requestHandler) logger.Infof("started VictoriaLogs in %.3f seconds; see https://docs.victoriametrics.com/VictoriaLogs/", time.Since(startTime).Seconds()) pushmetrics.Init() @@ -50,9 +54,9 @@ func main() { logger.Infof("received signal %s", sig) pushmetrics.Stop() - logger.Infof("gracefully shutting down webservice at %q", *httpListenAddr) + logger.Infof("gracefully shutting down webservice at %q", listenAddrs) startTime = time.Now() - if err := httpserver.Stop(*httpListenAddr); err != nil { + if err := httpserver.Stop(listenAddrs); err != nil { logger.Fatalf("cannot stop the webservice: %s", err) } logger.Infof("successfully shut down the webservice in %.3f seconds", time.Since(startTime).Seconds()) diff --git a/app/victoria-metrics/main.go b/app/victoria-metrics/main.go index 46d564cc7..a98327e39 100644 --- a/app/victoria-metrics/main.go +++ b/app/victoria-metrics/main.go @@ -26,8 +26,8 @@ import ( ) var ( - httpListenAddr = flag.String("httpListenAddr", ":8428", "TCP address to listen for http connections. See also -tls and -httpListenAddr.useProxyProtocol") - useProxyProtocol = flag.Bool("httpListenAddr.useProxyProtocol", false, "Whether to use proxy protocol for connections accepted at -httpListenAddr . "+ + httpListenAddrs = flagutil.NewArrayString("httpListenAddr", "TCP addresses to listen for incoming http requests. See also -tls and -httpListenAddr.useProxyProtocol") + useProxyProtocol = flagutil.NewArrayBool("httpListenAddr.useProxyProtocol", "Whether to use proxy protocol for connections accepted at the corresponding -httpListenAddr . "+ "See https://www.haproxy.org/download/1.8/doc/proxy-protocol.txt . "+ "With enabled proxy protocol http server cannot serve regular /metrics endpoint. Use -pushmetrics.url for metrics pushing") minScrapeInterval = flag.Duration("dedup.minScrapeInterval", 0, "Leave only the last sample in every time series per each discrete interval "+ @@ -66,7 +66,11 @@ func main() { return } - logger.Infof("starting VictoriaMetrics at %q...", *httpListenAddr) + listenAddrs := *httpListenAddrs + if len(listenAddrs) == 0 { + listenAddrs = []string{":8428"} + } + logger.Infof("starting VictoriaMetrics at %q...", listenAddrs) startTime := time.Now() storage.SetDedupInterval(*minScrapeInterval) storage.SetDataFlushInterval(*inmemoryDataFlushInterval) @@ -76,7 +80,7 @@ func main() { startSelfScraper() - go httpserver.Serve(*httpListenAddr, *useProxyProtocol, requestHandler) + go httpserver.Serve(listenAddrs, useProxyProtocol, requestHandler) logger.Infof("started VictoriaMetrics in %.3f seconds", time.Since(startTime).Seconds()) pushmetrics.Init() @@ -86,9 +90,9 @@ func main() { stopSelfScraper() - logger.Infof("gracefully shutting down webservice at %q", *httpListenAddr) + logger.Infof("gracefully shutting down webservice at %q", listenAddrs) startTime = time.Now() - if err := httpserver.Stop(*httpListenAddr); err != nil { + if err := httpserver.Stop(listenAddrs); err != nil { logger.Fatalf("cannot stop the webservice: %s", err) } logger.Infof("successfully shut down the webservice in %.3f seconds", time.Since(startTime).Seconds()) diff --git a/app/victoria-metrics/main_test.go b/app/victoria-metrics/main_test.go index 9a9075a89..0bba4a29f 100644 --- a/app/victoria-metrics/main_test.go +++ b/app/victoria-metrics/main_test.go @@ -180,7 +180,7 @@ func setUp() { vmstorage.Init(promql.ResetRollupResultCacheIfNeeded) vmselect.Init() vminsert.Init() - go httpserver.Serve(*httpListenAddr, false, requestHandler) + go httpserver.Serve(*httpListenAddrs, useProxyProtocol, requestHandler) readyStorageCheckFunc := func() bool { resp, err := http.Get(testHealthHTTPPath) if err != nil { @@ -226,7 +226,7 @@ func waitFor(timeout time.Duration, f func() bool) error { } func tearDown() { - if err := httpserver.Stop(*httpListenAddr); err != nil { + if err := httpserver.Stop(*httpListenAddrs); err != nil { log.Printf("cannot stop the webservice: %s", err) } vminsert.Stop() diff --git a/app/vmagent/main.go b/app/vmagent/main.go index 8e77afea2..7eedc3a24 100644 --- a/app/vmagent/main.go +++ b/app/vmagent/main.go @@ -46,10 +46,10 @@ import ( ) var ( - httpListenAddr = flag.String("httpListenAddr", ":8429", "TCP address to listen for http connections. "+ + httpListenAddrs = flagutil.NewArrayString("httpListenAddr", "TCP address to listen for incoming http requests. "+ "Set this flag to empty value in order to disable listening on any port. This mode may be useful for running multiple vmagent instances on the same server. "+ "Note that /targets and /metrics pages aren't available if -httpListenAddr=''. See also -tls and -httpListenAddr.useProxyProtocol") - useProxyProtocol = flag.Bool("httpListenAddr.useProxyProtocol", false, "Whether to use proxy protocol for connections accepted at -httpListenAddr . "+ + useProxyProtocol = flagutil.NewArrayBool("httpListenAddr.useProxyProtocol", "Whether to use proxy protocol for connections accepted at the corresponding -httpListenAddr . "+ "See https://www.haproxy.org/download/1.8/doc/proxy-protocol.txt . "+ "With enabled proxy protocol http server cannot serve regular /metrics endpoint. Use -pushmetrics.url for metrics pushing") influxListenAddr = flag.String("influxListenAddr", "", "TCP and UDP address to listen for InfluxDB line protocol data. Usually :8089 must be set. Doesn't work if empty. "+ @@ -120,7 +120,11 @@ func main() { return } - logger.Infof("starting vmagent at %q...", *httpListenAddr) + listenAddrs := *httpListenAddrs + if len(listenAddrs) == 0 { + listenAddrs = []string{":8429"} + } + logger.Infof("starting vmagent at %q...", listenAddrs) startTime := time.Now() remotewrite.Init() common.StartUnmarshalWorkers() @@ -143,9 +147,7 @@ func main() { promscrape.Init(remotewrite.PushDropSamplesOnFailure) - if len(*httpListenAddr) > 0 { - go httpserver.Serve(*httpListenAddr, *useProxyProtocol, requestHandler) - } + go httpserver.Serve(listenAddrs, useProxyProtocol, requestHandler) logger.Infof("started vmagent in %.3f seconds", time.Since(startTime).Seconds()) pushmetrics.Init() @@ -154,13 +156,11 @@ func main() { pushmetrics.Stop() startTime = time.Now() - if len(*httpListenAddr) > 0 { - logger.Infof("gracefully shutting down webservice at %q", *httpListenAddr) - if err := httpserver.Stop(*httpListenAddr); err != nil { - logger.Fatalf("cannot stop the webservice: %s", err) - } - logger.Infof("successfully shut down the webservice in %.3f seconds", time.Since(startTime).Seconds()) + logger.Infof("gracefully shutting down webservice at %q", listenAddrs) + if err := httpserver.Stop(listenAddrs); err != nil { + logger.Fatalf("cannot stop the webservice: %s", err) } + logger.Infof("successfully shut down the webservice in %.3f seconds", time.Since(startTime).Seconds()) promscrape.Stop() diff --git a/app/vmalert-tool/unittest/unittest.go b/app/vmalert-tool/unittest/unittest.go index fcf4a0019..aaf61af68 100644 --- a/app/vmalert-tool/unittest/unittest.go +++ b/app/vmalert-tool/unittest/unittest.go @@ -25,6 +25,7 @@ import ( "github.com/VictoriaMetrics/VictoriaMetrics/app/vmselect/prometheus" "github.com/VictoriaMetrics/VictoriaMetrics/app/vmselect/promql" "github.com/VictoriaMetrics/VictoriaMetrics/app/vmstorage" + "github.com/VictoriaMetrics/VictoriaMetrics/lib/flagutil" "github.com/VictoriaMetrics/VictoriaMetrics/lib/fs" "github.com/VictoriaMetrics/VictoriaMetrics/lib/httpserver" "github.com/VictoriaMetrics/VictoriaMetrics/lib/logger" @@ -184,7 +185,8 @@ func processFlags() { func setUp() { vmstorage.Init(promql.ResetRollupResultCacheIfNeeded) - go httpserver.Serve(httpListenAddr, false, func(w http.ResponseWriter, r *http.Request) bool { + var ab flagutil.ArrayBool + go httpserver.Serve([]string{httpListenAddr}, &ab, func(w http.ResponseWriter, r *http.Request) bool { switch r.URL.Path { case "/prometheus/api/v1/query": if err := prometheus.QueryHandler(nil, time.Now(), w, r); err != nil { @@ -225,7 +227,7 @@ checkCheck: } func tearDown() { - if err := httpserver.Stop(httpListenAddr); err != nil { + if err := httpserver.Stop([]string{httpListenAddr}); err != nil { logger.Errorf("cannot stop the webservice: %s", err) } vmstorage.Stop() diff --git a/app/vmalert/main.go b/app/vmalert/main.go index c2e43c280..fbbbffd7b 100644 --- a/app/vmalert/main.go +++ b/app/vmalert/main.go @@ -59,8 +59,8 @@ absolute path to all .tpl files in root. configCheckInterval = flag.Duration("configCheckInterval", 0, "Interval for checking for changes in '-rule' or '-notifier.config' files. "+ "By default, the checking is disabled. Send SIGHUP signal in order to force config check for changes.") - httpListenAddr = flag.String("httpListenAddr", ":8880", "Address to listen for http connections. See also -tls and -httpListenAddr.useProxyProtocol") - useProxyProtocol = flag.Bool("httpListenAddr.useProxyProtocol", false, "Whether to use proxy protocol for connections accepted at -httpListenAddr . "+ + httpListenAddrs = flagutil.NewArrayString("httpListenAddr", "Address to listen for incoming http requests. See also -tls and -httpListenAddr.useProxyProtocol") + useProxyProtocol = flagutil.NewArrayBool("httpListenAddr.useProxyProtocol", "Whether to use proxy protocol for connections accepted at the corresponding -httpListenAddr . "+ "See https://www.haproxy.org/download/1.8/doc/proxy-protocol.txt . "+ "With enabled proxy protocol http server cannot serve regular /metrics endpoint. Use -pushmetrics.url for metrics pushing") evaluationInterval = flag.Duration("evaluationInterval", time.Minute, "How often to evaluate the rules") @@ -178,15 +178,19 @@ func main() { go configReload(ctx, manager, groupsCfg, sighupCh) + listenAddrs := *httpListenAddrs + if len(listenAddrs) == 0 { + listenAddrs = []string{":8880"} + } rh := &requestHandler{m: manager} - go httpserver.Serve(*httpListenAddr, *useProxyProtocol, rh.handler) + go httpserver.Serve(listenAddrs, useProxyProtocol, rh.handler) pushmetrics.Init() sig := procutil.WaitForSigterm() logger.Infof("service received signal %s", sig) pushmetrics.Stop() - if err := httpserver.Stop(*httpListenAddr); err != nil { + if err := httpserver.Stop(listenAddrs); err != nil { logger.Fatalf("cannot stop the webservice: %s", err) } cancel() @@ -248,7 +252,13 @@ func newManager(ctx context.Context) (*manager, error) { func getExternalURL(customURL string) (*url.URL, error) { if customURL == "" { // use local hostname as external URL - return getHostnameAsExternalURL(*httpListenAddr, httpserver.IsTLS()) + listenAddr := ":8880" + if len(*httpListenAddrs) > 0 { + listenAddr = (*httpListenAddrs)[0] + } + isTLS := httpserver.IsTLS(0) + + return getHostnameAsExternalURL(listenAddr, isTLS) } u, err := url.Parse(customURL) if err != nil { @@ -260,13 +270,13 @@ func getExternalURL(customURL string) (*url.URL, error) { return u, nil } -func getHostnameAsExternalURL(httpListenAddr string, isSecure bool) (*url.URL, error) { +func getHostnameAsExternalURL(addr string, isSecure bool) (*url.URL, error) { hname, err := os.Hostname() if err != nil { return nil, fmt.Errorf("failed to get hostname: %w", err) } port := "" - if ipport := strings.Split(httpListenAddr, ":"); len(ipport) > 1 { + if ipport := strings.Split(addr, ":"); len(ipport) > 1 { port = ":" + ipport[1] } schema := "http://" diff --git a/app/vmauth/main.go b/app/vmauth/main.go index c92ee2056..a31845ff2 100644 --- a/app/vmauth/main.go +++ b/app/vmauth/main.go @@ -33,8 +33,8 @@ import ( ) var ( - httpListenAddr = flag.String("httpListenAddr", ":8427", "TCP address to listen for http connections. See also -tls and -httpListenAddr.useProxyProtocol") - useProxyProtocol = flag.Bool("httpListenAddr.useProxyProtocol", false, "Whether to use proxy protocol for connections accepted at -httpListenAddr . "+ + httpListenAddrs = flagutil.NewArrayString("httpListenAddr", "TCP address to listen for incoming http requests. See also -tls and -httpListenAddr.useProxyProtocol") + useProxyProtocol = flagutil.NewArrayBool("httpListenAddr.useProxyProtocol", "Whether to use proxy protocol for connections accepted at the corresponding -httpListenAddr . "+ "See https://www.haproxy.org/download/1.8/doc/proxy-protocol.txt . "+ "With enabled proxy protocol http server cannot serve regular /metrics endpoint. Use -pushmetrics.url for metrics pushing") maxIdleConnsPerBackend = flag.Int("maxIdleConnsPerBackend", 100, "The maximum number of idle connections vmauth can open per each backend host. "+ @@ -65,10 +65,14 @@ func main() { buildinfo.Init() logger.Init() - logger.Infof("starting vmauth at %q...", *httpListenAddr) + listenAddrs := *httpListenAddrs + if len(listenAddrs) == 0 { + listenAddrs = []string{":8427"} + } + logger.Infof("starting vmauth at %q...", listenAddrs) startTime := time.Now() initAuthConfig() - go httpserver.Serve(*httpListenAddr, *useProxyProtocol, requestHandler) + go httpserver.Serve(listenAddrs, useProxyProtocol, requestHandler) logger.Infof("started vmauth in %.3f seconds", time.Since(startTime).Seconds()) pushmetrics.Init() @@ -77,8 +81,8 @@ func main() { pushmetrics.Stop() startTime = time.Now() - logger.Infof("gracefully shutting down webservice at %q", *httpListenAddr) - if err := httpserver.Stop(*httpListenAddr); err != nil { + logger.Infof("gracefully shutting down webservice at %q", listenAddrs) + if err := httpserver.Stop(listenAddrs); err != nil { logger.Fatalf("cannot stop the webservice: %s", err) } logger.Infof("successfully shut down the webservice in %.3f seconds", time.Since(startTime).Seconds()) diff --git a/app/vmbackup/main.go b/app/vmbackup/main.go index 2be998aab..e6c92d0e9 100644 --- a/app/vmbackup/main.go +++ b/app/vmbackup/main.go @@ -93,7 +93,8 @@ func main() { } } - go httpserver.Serve(*httpListenAddr, false, nil) + listenAddrs := []string{*httpListenAddr} + go httpserver.Serve(listenAddrs, nil, nil) pushmetrics.Init() err := makeBackup() @@ -104,8 +105,8 @@ func main() { pushmetrics.Stop() startTime := time.Now() - logger.Infof("gracefully shutting down http server for metrics at %q", *httpListenAddr) - if err := httpserver.Stop(*httpListenAddr); err != nil { + logger.Infof("gracefully shutting down http server for metrics at %q", listenAddrs) + if err := httpserver.Stop(listenAddrs); err != nil { logger.Fatalf("cannot stop http server for metrics: %s", err) } logger.Infof("successfully shut down http server for metrics in %.3f seconds", time.Since(startTime).Seconds()) diff --git a/app/vmrestore/main.go b/app/vmrestore/main.go index c0389bc43..98448ddca 100644 --- a/app/vmrestore/main.go +++ b/app/vmrestore/main.go @@ -37,7 +37,8 @@ func main() { buildinfo.Init() logger.Init() - go httpserver.Serve(*httpListenAddr, false, nil) + listenAddrs := []string{*httpListenAddr} + go httpserver.Serve(listenAddrs, nil, nil) srcFS, err := newSrcFS() if err != nil { @@ -62,8 +63,8 @@ func main() { dstFS.MustStop() startTime := time.Now() - logger.Infof("gracefully shutting down http server for metrics at %q", *httpListenAddr) - if err := httpserver.Stop(*httpListenAddr); err != nil { + logger.Infof("gracefully shutting down http server for metrics at %q", listenAddrs) + if err := httpserver.Stop(listenAddrs); err != nil { logger.Fatalf("cannot stop http server for metrics: %s", err) } logger.Infof("successfully shut down http server for metrics in %.3f seconds", time.Since(startTime).Seconds()) diff --git a/docs/CHANGELOG.md b/docs/CHANGELOG.md index 0451f7432..bf8186beb 100644 --- a/docs/CHANGELOG.md +++ b/docs/CHANGELOG.md @@ -31,6 +31,7 @@ The sandbox cluster installation is running under the constant load generated by * SECURITY: upgrade Go builder from Go1.21.6 to Go1.21.7. See [the list of issues addressed in Go1.21.7](https://github.com/golang/go/issues?q=milestone%3AGo1.21.7+label%3ACherryPickApproved). * FEATURE: all VictoriaMetrics components: add support for TLS client certificate verification at `-httpListenAddr` (aka [mTLS](https://en.wikipedia.org/wiki/Mutual_authentication)). See [these docs](https://docs.victoriametrics.com/#mtls-protection). See [this feature request](https://github.com/VictoriaMetrics/VictoriaMetrics/issues/5458). +* FEATURE: all VictoriaMetrics components: add support for accepting http requests over multiple distinct TCP addresses by starting VictoriaMetrics component with multiple `-httpListenAddr` command-line flags. For example, `./victoria-metrics -httpListenAddr=some-host:12345 -httpListenAddr=localhost:8428` starts VictoriaMetrics, which accepts incoming http requests at both `some-host:12345` and `localhost:8428`. See [this feature request](https://github.com/VictoriaMetrics/VictoriaMetrics/issues/1470). * FEATURE: all VictoriaMetrics components: add support for empty command flag values in short array notation. For example, `-remoteWrite.sendTimeout=',20s,'` specifies three `-remoteWrite.sendTimeout` values - the first and the last ones are default values (`30s` in this case), while the second one is `20s`. * FEATURE: all VictoriaMetrics components: do not close connections to `-httpListenAddr` every 2 minutes. This behavior didn't help spreading load among multiple backend servers behind load-balancing TCP proxy. Instead, it could lead to hard-to-debug issues like [this one](https://github.com/VictoriaMetrics/VictoriaMetrics/issues/1304#issuecomment-1636997037). If you still need periodically closing client connections because of some reason, then pass the desired timeout to `-http.connTimeout` command-line flag. * FEATURE: [vmagent](https://docs.victoriametrics.com/vmagent.html) and [single-node VictoriaMetrics](https://docs.victoriametrics.com): add support for data ingestion via [DataDog lambda extension](https://docs.datadoghq.com/serverless/libraries_integrations/extension/) aka `/api/beta/sketches` endpoint. See [these docs](https://docs.victoriametrics.com/#how-to-send-data-from-datadog-agent) and [this feature request](https://github.com/VictoriaMetrics/VictoriaMetrics/issues/3091). Thanks to @AndrewChubatiuk for [the pull request](https://github.com/VictoriaMetrics/VictoriaMetrics/pull/5584). diff --git a/lib/flagutil/array.go b/lib/flagutil/array.go index 324596aa1..fb086004f 100644 --- a/lib/flagutil/array.go +++ b/lib/flagutil/array.go @@ -101,8 +101,8 @@ func (a *ArrayString) Set(value string) error { } func parseArrayValues(s string) []string { - if len(s) == 0 { - return nil + if s == "" { + return []string{""} } var values []string for { diff --git a/lib/flagutil/array_test.go b/lib/flagutil/array_test.go index 6bb884fa6..4c908894b 100644 --- a/lib/flagutil/array_test.go +++ b/lib/flagutil/array_test.go @@ -174,7 +174,7 @@ func TestArrayDuration_Set(t *testing.T) { t.Fatalf("unexpected values parsed;\ngot\n%q\nwant\n%q", result, expectedResult) } } - f("", "") + f("", "42s") f(`1m`, `1m0s`) f(`5m,1s,1h`, `5m0s,1s,1h0m0s`) f(`5m,,1h`, `5m0s,42s,1h0m0s`) @@ -211,7 +211,6 @@ func TestArrayDuration_String(t *testing.T) { t.Fatalf("unexpected string;\ngot\n%s\nwant\n%s", result, s) } } - f("") f("10s,1m0s") f("5m0s,1s") } @@ -237,7 +236,7 @@ func TestArrayBool_Set(t *testing.T) { t.Fatalf("unexpected values parsed;\ngot\n%v\nwant\n%v", result, expectedResult) } } - f("", "") + f("", "false") f(`true`, `true`) f(`false,True,False`, `false,true,false`) f(`1,,False`, `true,false,false`) @@ -273,7 +272,6 @@ func TestArrayBool_String(t *testing.T) { t.Fatalf("unexpected string;\ngot\n%s\nwant\n%s", result, s) } } - f("") f("true") f("true,false") f("false,true") @@ -305,7 +303,7 @@ func TestArrayInt_Set(t *testing.T) { t.Fatalf("unexpected values;\ngot\n%d\nwant\n%d", values, expectedValues) } } - f("", "", nil) + f("", "42", []int{42}) f(`1`, `1`, []int{1}) f(`-2,3,-64`, `-2,3,-64`, []int{-2, 3, -64}) f(`,,-64,`, `42,42,-64,42`, []int{42, 42, -64, 42}) @@ -342,7 +340,6 @@ func TestArrayInt_String(t *testing.T) { t.Fatalf("unexpected string;\ngot\n%s\nwant\n%s", result, s) } } - f("") f("10,1") f("-5,1,123") } @@ -371,7 +368,7 @@ func TestArrayBytes_Set(t *testing.T) { t.Fatalf("unexpected values parsed;\ngot\n%s\nwant\n%s", result, expectedResult) } } - f("", "") + f("", "42") f(`1`, `1`) f(`-2,3,10kb`, `-2,3,10KB`) f(`,,10kb`, `42,42,10KB`) @@ -409,7 +406,6 @@ func TestArrayBytes_String(t *testing.T) { t.Fatalf("unexpected string;\ngot\n%s\nwant\n%s", result, s) } } - f("") f("10.5KiB,1") f("-5,1,123MB") } diff --git a/lib/flagutil/dict_test.go b/lib/flagutil/dict_test.go index ef66e5ae1..d1aebb1dc 100644 --- a/lib/flagutil/dict_test.go +++ b/lib/flagutil/dict_test.go @@ -64,7 +64,6 @@ func TestDictIntSetSuccess(t *testing.T) { } } - f("") f("123") f("-234") f("foo:123") @@ -107,8 +106,6 @@ func TestDictIntGet(t *testing.T) { } } - f("", "", 123, 123) - f("", "foo", 123, 123) f("foo:42", "", 123, 123) f("foo:42", "foo", 123, 42) f("532", "", 123, 532) diff --git a/lib/httpserver/httpserver.go b/lib/httpserver/httpserver.go index 451a10aac..cade0bcdd 100644 --- a/lib/httpserver/httpserver.go +++ b/lib/httpserver/httpserver.go @@ -31,12 +31,14 @@ import ( ) var ( - tlsEnable = flag.Bool("tls", false, "Whether to enable TLS for incoming HTTP requests at -httpListenAddr (aka https). -tlsCertFile and -tlsKeyFile must be set if -tls is set. "+ + tlsEnable = flagutil.NewArrayBool("tls", "Whether to enable TLS for incoming HTTP requests at the given -httpListenAddr (aka https). -tlsCertFile and -tlsKeyFile must be set if -tls is set. "+ "See also -mtls") - tlsCertFile = flag.String("tlsCertFile", "", "Path to file with TLS certificate if -tls is set. Prefer ECDSA certs instead of RSA certs as RSA certs are slower. The provided certificate file is automatically re-read every second, so it can be dynamically updated") - tlsKeyFile = flag.String("tlsKeyFile", "", "Path to file with TLS key if -tls is set. The provided key file is automatically re-read every second, so it can be dynamically updated") + tlsCertFile = flagutil.NewArrayString("tlsCertFile", "Path to file with TLS certificate for the corresponding -httpListenAddr if -tls is set. "+ + "Prefer ECDSA certs instead of RSA certs as RSA certs are slower. The provided certificate file is automatically re-read every second, so it can be dynamically updated") + tlsKeyFile = flagutil.NewArrayString("tlsKeyFile", "Path to file with TLS key for the corresponding -httpListenAddr if -tls is set. "+ + "The provided key file is automatically re-read every second, so it can be dynamically updated") tlsCipherSuites = flagutil.NewArrayString("tlsCipherSuites", "Optional list of TLS cipher suites for incoming requests over HTTPS if -tls is set. See the list of supported cipher suites at https://pkg.go.dev/crypto/tls#pkg-constants") - tlsMinVersion = flag.String("tlsMinVersion", "", "Optional minimum TLS version to use for incoming requests over HTTPS if -tls is set. "+ + tlsMinVersion = flagutil.NewArrayString("tlsMinVersion", "Optional minimum TLS version to use for the corresponding -httpListenAddr if -tls is set. "+ "Supported values: TLS10, TLS11, TLS12, TLS13") pathPrefix = flag.String("http.pathPrefix", "", "An optional prefix to add to all the paths handled by http server. For example, if '-http.pathPrefix=/foo/bar' is set, "+ @@ -77,35 +79,51 @@ type server struct { // In such cases the caller must serve the request. type RequestHandler func(w http.ResponseWriter, r *http.Request) bool -// Serve starts an http server on the given addr with the given optional rh. +// Serve starts an http server on the given addrs with the given optional rh. // // By default all the responses are transparently compressed, since egress traffic is usually expensive. // -// The compression is also disabled if -http.disableResponseCompression flag is set. +// The compression can be disabled by specifying -http.disableResponseCompression command-line flag. // -// If useProxyProtocol is set to true, then the incoming connections are accepted via proxy protocol. +// If useProxyProtocol is set to true for the corresponding addr, then the incoming connections are accepted via proxy protocol. // See https://www.haproxy.org/download/1.8/doc/proxy-protocol.txt -func Serve(addr string, useProxyProtocol bool, rh RequestHandler) { +func Serve(addrs []string, useProxyProtocol *flagutil.ArrayBool, rh RequestHandler) { if rh == nil { rh = func(w http.ResponseWriter, r *http.Request) bool { return false } } + for idx, addr := range addrs { + if addr == "" { + continue + } + useProxyProto := false + if useProxyProtocol != nil { + useProxyProto = useProxyProtocol.GetOptionalArg(idx) + } + go serve(addr, useProxyProto, rh, idx) + } +} + +func serve(addr string, useProxyProtocol bool, rh RequestHandler, idx int) { scheme := "http" - if *tlsEnable { + if tlsEnable.GetOptionalArg(idx) { scheme = "https" } hostAddr := addr if strings.HasPrefix(hostAddr, ":") { hostAddr = "127.0.0.1" + hostAddr } - logger.Infof("starting http server at %s://%s/", scheme, hostAddr) + logger.Infof("starting server at %s://%s/", scheme, hostAddr) logger.Infof("pprof handlers are exposed at %s://%s/debug/pprof/", scheme, hostAddr) var tlsConfig *tls.Config - if *tlsEnable { - tc, err := netutil.GetServerTLSConfig(*tlsCertFile, *tlsKeyFile, *tlsMinVersion, *tlsCipherSuites) + if tlsEnable.GetOptionalArg(idx) { + certFile := tlsCertFile.GetOptionalArg(idx) + keyFile := tlsKeyFile.GetOptionalArg(idx) + minVersion := tlsMinVersion.GetOptionalArg(idx) + tc, err := netutil.GetServerTLSConfig(certFile, keyFile, minVersion, *tlsCipherSuites) if err != nil { - logger.Fatalf("cannot load TLS cert from -tlsCertFile=%q, -tlsKeyFile=%q, -tlsMinVersion=%q: %s", *tlsCertFile, *tlsKeyFile, *tlsMinVersion, err) + logger.Fatalf("cannot load TLS cert from -tlsCertFile=%q, -tlsKeyFile=%q, -tlsMinVersion=%q, -tlsCipherSuites=%q: %s", certFile, keyFile, minVersion, *tlsCipherSuites, err) } tlsConfig = tc } @@ -168,15 +186,38 @@ func whetherToCloseConn(r *http.Request) bool { var connDeadlineTimeKey = interface{}("connDeadlineSecs") -// Stop stops the http server on the given addr, which has been started -// via Serve func. -func Stop(addr string) error { +// Stop stops the http server on the given addrs, which has been started via Serve func. +func Stop(addrs []string) error { + var errGlobalLock sync.Mutex + var errGlobal error + + var wg sync.WaitGroup + for _, addr := range addrs { + if addr == "" { + continue + } + wg.Add(1) + go func(addr string) { + if err := stop(addr); err != nil { + errGlobalLock.Lock() + errGlobal = err + errGlobalLock.Unlock() + } + wg.Done() + }(addr) + } + wg.Wait() + + return errGlobal +} + +func stop(addr string) error { serversLock.Lock() s := servers[addr] delete(servers, addr) serversLock.Unlock() if s == nil { - err := fmt.Errorf("BUG: there is no http server at %q", addr) + err := fmt.Errorf("BUG: there is no server at %q", addr) logger.Panicf("%s", err) // The return is needed for golangci-lint: SA5011(related information): this check suggests that the pointer can be nil return err @@ -593,9 +634,9 @@ func (e *ErrorWithStatusCode) Error() string { return e.Err.Error() } -// IsTLS indicates is tls enabled or not -func IsTLS() bool { - return *tlsEnable +// IsTLS indicates is tls enabled or not for -httpListenAddr at the given idx. +func IsTLS(idx int) bool { + return tlsEnable.GetOptionalArg(idx) } // GetPathPrefix - returns http server path prefix.