app/vmauth: allow to disable built-in routes

vmauth uses 'lib/httpserver' for serving HTTP requests. This server
 unconditionally defines built-in routes (such as '/metrics',
 '/health', etc). It makes impossible to proxy `HTTP` requests to  backends with the same routes.
 Since vmauth's httpserver matches built-in route and return local
 response.

  This commit adds new flag `httpListenAddr.disableBuiltinRoutes` with
 default value of false, which allows to disable built-in routes per httpListenAddr.

  It's recommend to disable built-in routes for the main httpListenAddr
and expose it at the additional httpListenAddr.

 For example given configuration disables built-in routes at `0.0.0.0:8427` address and serves it at `0.0.0.0:8426`:

 `./bin/vmauth --auth.config=config.yaml --httpListenAddr=:8427,:8426 --httpListenAddr.disableBuiltinRoutes=true,false`

 Related issues:
 - https://github.com/VictoriaMetrics/VictoriaMetrics/issues/6468
 - https://github.com/VictoriaMetrics/VictoriaMetrics/issues/7345

Signed-off-by: f41gh7 <nik@victoriametrics.com>
This commit is contained in:
f41gh7 2024-11-27 17:03:11 +01:00
parent be24fbe8ae
commit 691561ab6f
No known key found for this signature in database
GPG key ID: 4558311CF775EC72
9 changed files with 327 additions and 58 deletions

View file

@ -35,6 +35,9 @@ var (
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")
disableBuiltinRoutes = flagutil.NewArrayBool("httpListenAddr.disableBuiltinRoutes", "Whether to disable built-in routes for the corresponding -httpListenAddr. "+
"This option allows proper routing of requests to backends with the same built-in routes, such as /metrics. "+
"It is recommended to disable built-in routes for the main -httpListenAddr and expose them via an additional -httpListenAddr.")
maxIdleConnsPerBackend = flag.Int("maxIdleConnsPerBackend", 100, "The maximum number of idle connections vmauth can open per each backend host. "+
"See also -maxConcurrentRequests")
idleConnTimeout = flag.Duration("idleConnTimeout", 50*time.Second, "The timeout for HTTP keep-alive connections to backend services. "+
@ -91,7 +94,12 @@ func main() {
logger.Infof("starting vmauth at %q...", listenAddrs)
startTime := time.Now()
initAuthConfig()
go httpserver.Serve(listenAddrs, useProxyProtocol, requestHandler)
serveOpts := &httpserver.ServeOptions{
UseProxyProtocol: useProxyProtocol,
DisableBuiltinRoutes: disableBuiltinRoutes,
}
rhs := buildRequestHandlers(listenAddrs)
go httpserver.ServeWithOpts(listenAddrs, rhs, serveOpts)
logger.Infof("started vmauth in %.3f seconds", time.Since(startTime).Seconds())
pushmetrics.Init()
@ -109,7 +117,19 @@ func main() {
logger.Infof("successfully stopped vmauth in %.3f seconds", time.Since(startTime).Seconds())
}
func requestHandler(w http.ResponseWriter, r *http.Request) bool {
func buildRequestHandlers(listenAddrs []string) []httpserver.RequestHandler {
rhs := make([]httpserver.RequestHandler, len(listenAddrs))
for idx := range listenAddrs {
rh := requestHandlerWithPrivateRoutes
if disableBuiltinRoutes.GetOptionalArg(idx) {
rh = requestHandler
}
rhs[idx] = rh
}
return rhs
}
func requestHandlerWithPrivateRoutes(w http.ResponseWriter, r *http.Request) bool {
switch r.URL.Path {
case "/-/reload":
if !httpserver.CheckAuthFlag(w, r, reloadAuthKey) {
@ -120,6 +140,10 @@ func requestHandler(w http.ResponseWriter, r *http.Request) bool {
w.WriteHeader(http.StatusOK)
return true
}
return requestHandler(w, r)
}
func requestHandler(w http.ResponseWriter, r *http.Request) bool {
ats := getAuthTokensFromRequest(r)
if len(ats) == 0 {

View file

@ -52,7 +52,7 @@ func TestRequestHandler(t *testing.T) {
r.Header.Set("Pass-Header", "abc")
w := &fakeResponseWriter{}
if !requestHandler(w, r) {
if !requestHandlerWithPrivateRoutes(w, r) {
t.Fatalf("unexpected false is returned from requestHandler")
}

View file

@ -2,6 +2,8 @@ package apptest
import (
"fmt"
"os"
"path"
"testing"
"time"
@ -134,6 +136,23 @@ func (c *vmcluster) ForceFlush(t *testing.T) {
}
}
// MustStartVmauth is a test helper function that starts an instance of
// vmauth and fails the test if the app fails to start.
func (tc *TestCase) MustStartVmauth(instance string, flags []string, configFileYAML string) *Vmauth {
tc.t.Helper()
configFilePath := path.Join(tc.t.TempDir(), "config.yaml")
if err := os.WriteFile(configFilePath, []byte(configFileYAML), os.ModePerm); err != nil {
tc.t.Fatalf("cannot init vmauth: config file write failed: %s", err)
}
app, err := StartVmauth(instance, flags, tc.cli, configFilePath)
if err != nil {
tc.t.Fatalf("Could not start %s: %v", instance, err)
}
tc.addApp(instance, app)
return app
}
// MustStartDefaultCluster starts a typical cluster configuration with default
// flags.
func (tc *TestCase) MustStartDefaultCluster() PrometheusWriteQuerier {

View file

@ -0,0 +1,77 @@
package tests
import (
"fmt"
"io"
"net/http"
"net/http/httptest"
"testing"
"github.com/VictoriaMetrics/VictoriaMetrics/apptest"
)
func TestVMAuthRouting(t *testing.T) {
tc := apptest.NewTestCase(t)
defer tc.Stop()
var proxiedRequestsCount int
backend := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, _ *http.Request) {
w.WriteHeader(http.StatusOK)
proxiedRequestsCount++
}))
defer backend.Close()
authConfig := fmt.Sprintf(`
unauthorized_user:
url_prefix: %s
`, backend.URL)
const (
// it's not possible to use random ports
// it makes test flaky
listenPortNoBuiltin = "50128"
listenPortWithBuiltin = "50127"
)
vmauthFlags := []string{
fmt.Sprintf("-httpListenAddr=127.0.0.1:%s,127.0.0.1:%s", listenPortWithBuiltin, listenPortNoBuiltin),
"-httpListenAddr.disableBuiltinRoutes=false,true",
"-flagsAuthKey=protected",
}
vmauth := tc.MustStartVmauth("vmauth",
vmauthFlags,
authConfig)
var hc http.Client
makeGetRequestExpectCode := func(targetURL string, expectCode int) {
t.Helper()
req, err := http.NewRequest("GET", targetURL, nil)
if err != nil {
t.Fatalf("cannot build http.Request for target=%q: %s", targetURL, err)
}
resp, err := hc.Do(req)
if err != nil {
t.Fatalf("unexpected http request error: %s", err)
}
defer resp.Body.Close()
if resp.StatusCode != expectCode {
responseText, err := io.ReadAll(resp.Body)
if err != nil {
t.Errorf("cannot read response body: %s", err)
}
t.Fatalf("unexpected http response code: %d, want: %d, response text: %s", resp.StatusCode, expectCode, responseText)
}
}
// built-in http server must reject request, since it protected with authKey
makeGetRequestExpectCode(fmt.Sprintf("http://127.0.0.1:%s/flags", listenPortWithBuiltin), http.StatusUnauthorized)
makeGetRequestExpectCode(fmt.Sprintf("http://127.0.0.1:%s/flags", listenPortNoBuiltin), http.StatusOK)
if proxiedRequestsCount != 1 {
t.Fatalf("expected to have 1 proxied request, got: %d", proxiedRequestsCount)
}
// reload config and ensure that it no longer proxy requests to the backend
vmauth.UpdateConfiguration(t, "")
makeGetRequestExpectCode(fmt.Sprintf("http://127.0.0.1:%s/flags", listenPortWithBuiltin), http.StatusUnauthorized)
if proxiedRequestsCount != 1 {
t.Fatalf("expected to have 1 proxied request, got: %d", proxiedRequestsCount)
}
}

77
apptest/vmauth.go Normal file
View file

@ -0,0 +1,77 @@
package apptest
import (
"fmt"
"os"
"regexp"
"syscall"
"testing"
"time"
)
var httpBuilitinListenAddrRE = regexp.MustCompile(`pprof handlers are exposed at http://(.*:\d{1,5})/debug/pprof/`)
// Vmauth holds the state of a vmauth app and provides vmauth-specific
// functions.
type Vmauth struct {
*app
*ServesMetrics
httpListenAddr string
configFilePath string
cli *Client
}
// StartVmauth starts an instance of vmauth with the given flags. It also
// sets the default flags and populates the app instance state with runtime
// values extracted from the application log (such as httpListenAddr)
func StartVmauth(instance string, flags []string, cli *Client, configFilePath string) (*Vmauth, error) {
extractREs := []*regexp.Regexp{
httpBuilitinListenAddrRE,
}
app, stderrExtracts, err := startApp(instance, "../../bin/vmauth", flags, &appOptions{
defaultFlags: map[string]string{
"-httpListenAddr": "127.0.0.1:0",
"-auth.config": configFilePath,
},
extractREs: extractREs,
})
if err != nil {
return nil, err
}
return &Vmauth{
app: app,
ServesMetrics: &ServesMetrics{
metricsURL: fmt.Sprintf("http://%s/metrics", stderrExtracts[0]),
cli: cli,
},
httpListenAddr: stderrExtracts[0],
configFilePath: configFilePath,
cli: cli,
}, nil
}
// UpdateConfiguration performs configuration file reload for app and waits for configuration apply
//
// Due to second prescision of config reload metric, config cannot be reloaded more than 1 time in a second
func (app *Vmauth) UpdateConfiguration(t *testing.T, configFileYAML string) {
t.Helper()
ct := int(time.Now().Unix())
if err := os.WriteFile(app.configFilePath, []byte(configFileYAML), os.ModePerm); err != nil {
t.Fatalf("unexpected error at UpdateConfiguration, cannot write configFile content: %s", err)
}
if err := app.process.Signal(syscall.SIGHUP); err != nil {
t.Fatalf("unexpected signal error: %s", err)
}
for range 10 {
ts := app.GetIntMetric(t, "vmauth_config_last_reload_success_timestamp_seconds")
if ts < ct {
time.Sleep(time.Millisecond * 100)
continue
}
return
}
t.Fatalf("timeout waiting for config reload success")
}

View file

@ -48,6 +48,7 @@ Released at 2025-01-14
* FEATURE: [vmui](https://docs.victoriametrics.com/#vmui): add `markdown` support for comments during data export. [this pull request](https://github.com/VictoriaMetrics/VictoriaMetrics/pull/7828).
* FEATURE: [vmagent](https://docs.victoriametrics.com/vmagent/) and [Single-node VictoriaMetrics](https://docs.victoriametrics.com/): added `min` and `max` metrics for Datadog Sketches API metrics, changed `_` metric name separator to `.` if metrics are not sanitized for consistency.
* FEATURE: [vmagent](https://docs.victoriametrics.com/vmagent/) and [Single-node VictoriaMetrics](https://docs.victoriametrics.com/): add service discovery support for [Marathon](https://mesosphere.github.io/marathon/). See [these docs](https://docs.victoriametrics.com/sd_configs/#marathon_sd_configs) and [this issue](https://github.com/VictoriaMetrics/VictoriaMetrics/issues/6642).
* FEATURE: [vmauth](https://docs.victoriametrics.com/vmauth/): add `httpListenAddr.disableBuiltinRoutes` cmd-line flag to disable serving default HTTP routes `/metrics`, `/flags`, etc. It allows properly route requests to backends with the same service routes as vmauth. See [this issue](https://github.com/VictoriaMetrics/VictoriaMetrics/issues/6468) and [this issue](https://github.com/VictoriaMetrics/VictoriaMetrics/issues/7345) for details.
* FEATURE: [Single-node VictoriaMetrics](https://docs.victoriametrics.com/): support `-maxIngestionRate` cmd-line flag to ratelimit samples/sec ingested. See [this issue](https://github.com/VictoriaMetrics/VictoriaMetrics/issues/7377) for details.
* FEATURE: [vmsingle](https://docs.victoriametrics.com/single-server-victoriametrics/) and `vmselect` in [VictoriaMetrics cluster](https://docs.victoriametrics.com/cluster-victoriametrics/): improve query performance on systems with high number of CPU cores. See [this PR](https://github.com/VictoriaMetrics/VictoriaMetrics/pull/7416) for details.
* FEATURE: [vmsingle](https://docs.victoriametrics.com/single-server-victoriametrics/) and `vmselect` in [VictoriaMetrics cluster](https://docs.victoriametrics.com/cluster-victoriametrics/): add command-line flag `-search.maxBinaryOpPushdownLabelValues` to allow using labels with more candidate values as push down filter in binary operation. See [this pull request](https://github.com/VictoriaMetrics/VictoriaMetrics/pull/7243). Thanks to @tydhot for implementation.

View file

@ -1073,6 +1073,8 @@ It is recommended protecting the following endpoints with authKeys:
* `/metrics` with `-metricsAuthKey` command-line flag, so unauthorized users couldn't access [vmauth metrics](#monitoring).
* `/debug/pprof` with `-pprofAuthKey` command-line flag, so unauthorized users couldn't access [profiling information](#profiling).
As an alternative, it's possible to disable all built-in vmauth routes with command-line flag `-httpListenAddr.disableBuiltinRoutes=true`. This flag could have multiple values: `-httpListenAddr.disableBuiltinRoutes=true,false` for each corresponding `-httpListenAddr=0.0.0.0:8427,127.0.0.1:8426`.
`vmauth` also supports the ability to restrict access by IP - see [these docs](#ip-filters). See also [concurrency limiting docs](#concurrency-limiting).
## Automatic issuing of TLS certificates
@ -1281,6 +1283,11 @@ See the docs at https://docs.victoriametrics.com/vmauth/ .
TCP address to listen for incoming http requests. See also -tls and -httpListenAddr.useProxyProtocol
Supports an array of values separated by comma or specified via multiple flags.
Value can contain comma inside single-quoted or double-quoted string, {}, [] and () braces.
-httpListenAddr.disableBuiltinRoutes array
Whether to disable built-in routes for the corresponding -httpListenAddr.
This option allows proper routing of requests to backends with the same built-in routes, such as /metrics.
It is recommended to disable built-in routes for the main -httpListenAddr and expose them via an additional -httpListenAddr.
Empty values are set to false.
-httpListenAddr.useProxyProtocol array
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
Supports array of values separated by comma or specified via multiple flags.

View file

@ -83,6 +83,19 @@ type server struct {
// In such cases the caller must serve the request.
type RequestHandler func(w http.ResponseWriter, r *http.Request) bool
// ServeOptions defiens optional parameters for http server
type ServeOptions struct {
// UseProxyProtocol if 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
UseProxyProtocol *flagutil.ArrayBool
// DisableBuiltinRoutes if set to true for the corresponding addr, then the http will not serve built-in routes, such as:
// /health, /debug/pprof and few others
// In addition basic auth check and authKey checks will be disabled for the given addr
//
// Mostly required by http proxy servers, which peforms own authorization and requests routing
DisableBuiltinRoutes *flagutil.ArrayBool
}
// 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.
@ -97,23 +110,54 @@ func Serve(addrs []string, useProxyProtocol *flagutil.ArrayBool, rh RequestHandl
return false
}
}
opts := ServeOptions{
UseProxyProtocol: useProxyProtocol,
}
for idx, addr := range addrs {
if addr == "" {
continue
}
useProxyProto := false
if useProxyProtocol != nil {
useProxyProto = useProxyProtocol.GetOptionalArg(idx)
}
go serve(addr, useProxyProto, rh, idx)
go serve(addr, rh, idx, &opts)
}
}
func serve(addr string, useProxyProtocol bool, rh RequestHandler, idx int) {
// ServeWithOpts starts an http server on the given addrs with the given optional request handlers.
//
// By default all the responses are transparently compressed, since egress traffic is usually expensive.
//
// The compression can be disabled by specifying -http.disableResponseCompression command-line flag.
func ServeWithOpts(addrs []string, rhs []RequestHandler, opts *ServeOptions) {
if len(rhs) == 0 {
rhs = []RequestHandler{
func(_ http.ResponseWriter, _ *http.Request) bool {
return false
},
}
}
for idx, addr := range addrs {
if addr == "" {
continue
}
rh := rhs[0]
if idx < len(rhs) {
rh = rhs[idx]
}
go serve(addr, rh, idx, opts)
}
}
func serve(addr string, rh RequestHandler, idx int, opts *ServeOptions) {
scheme := "http"
if tlsEnable.GetOptionalArg(idx) {
scheme = "https"
}
useProxyProto, disableBuiltinRoutes := false, false
if opts.UseProxyProtocol != nil {
useProxyProto = opts.UseProxyProtocol.GetOptionalArg(idx)
}
if opts.DisableBuiltinRoutes != nil {
disableBuiltinRoutes = opts.DisableBuiltinRoutes.GetOptionalArg(idx)
}
var tlsConfig *tls.Config
if tlsEnable.GetOptionalArg(idx) {
certFile := tlsCertFile.GetOptionalArg(idx)
@ -125,20 +169,22 @@ func serve(addr string, useProxyProtocol bool, rh RequestHandler, idx int) {
}
tlsConfig = tc
}
ln, err := netutil.NewTCPListener(scheme, addr, useProxyProtocol, tlsConfig)
ln, err := netutil.NewTCPListener(scheme, addr, useProxyProto, tlsConfig)
if err != nil {
logger.Fatalf("cannot start http server at %s: %s", addr, err)
} else {
logger.Infof("started server at %s://%s/", scheme, ln.Addr())
}
logger.Infof("started server at %s://%s/", scheme, ln.Addr())
if !disableBuiltinRoutes {
logger.Infof("pprof handlers are exposed at %s://%s/debug/pprof/", scheme, ln.Addr())
}
serveWithListener(addr, ln, rh)
serveWithListener(addr, ln, rh, disableBuiltinRoutes)
}
func serveWithListener(addr string, ln net.Listener, rh RequestHandler) {
func serveWithListener(addr string, ln net.Listener, rh RequestHandler, disableBuiltinRoutes bool) {
var s server
s.s = &http.Server{
Handler: gzipHandler(&s, rh),
// Disable http/2, since it doesn't give any advantages for VictoriaMetrics services.
TLSNextProto: make(map[string]func(*http.Server, *tls.Conn, http.Handler)),
@ -162,6 +208,19 @@ func serveWithListener(addr string, ln net.Listener, rh RequestHandler) {
return context.WithValue(ctx, connDeadlineTimeKey, &deadline)
}
}
rhw := rh
if !disableBuiltinRoutes {
rhw = func(w http.ResponseWriter, r *http.Request) bool {
return builtinRoutesHandler(&s, r, w, rh)
}
}
h := http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
handlerWrapper(w, r, rhw)
})
if !*disableResponseCompression {
h = gzipHandlerWrapper(h)
}
s.s.Handler = h
serversLock.Lock()
servers[addr] = &s
@ -244,16 +303,6 @@ func stop(addr string) error {
return nil
}
func gzipHandler(s *server, rh RequestHandler) http.HandlerFunc {
h := http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
handlerWrapper(s, w, r, rh)
})
if *disableResponseCompression {
return h
}
return gzipHandlerWrapper(h)
}
var gzipHandlerWrapper = func() func(http.Handler) http.HandlerFunc {
hw, err := gzhttp.NewWrapper(gzhttp.CompressionLevel(1))
if err != nil {
@ -278,7 +327,7 @@ var hostname = func() string {
return h
}()
func handlerWrapper(s *server, w http.ResponseWriter, r *http.Request, rh RequestHandler) {
func handlerWrapper(w http.ResponseWriter, r *http.Request, rh RequestHandler) {
// All the VictoriaMetrics code assumes that panic stops the process.
// Unfortunately, the standard net/http.Server recovers from panics in request handlers,
// so VictoriaMetrics state can become inconsistent after the recovered panic.
@ -310,12 +359,7 @@ func handlerWrapper(s *server, w http.ResponseWriter, r *http.Request, rh Reques
h.Set("Connection", "close")
}
path := r.URL.Path
if strings.HasSuffix(path, "/favicon.ico") {
w.Header().Set("Cache-Control", "max-age=3600")
faviconRequests.Inc()
w.Write(faviconData)
return
}
prefix := GetPathPrefix()
if prefix != "" {
// Trim -http.pathPrefix from path
@ -336,13 +380,40 @@ func handlerWrapper(s *server, w http.ResponseWriter, r *http.Request, rh Reques
path = path[len(prefix)-1:]
r.URL.Path = path
}
w = &responseWriterWithAbort{
ResponseWriter: w,
}
if rh(w, r) {
return
}
Errorf(w, r, "unsupported path requested: %q", r.URL.Path)
unsupportedRequestErrors.Inc()
}
func builtinRoutesHandler(s *server, r *http.Request, w http.ResponseWriter, rh RequestHandler) bool {
if !isProtectedByAuthFlag(r.URL.Path) && !CheckBasicAuth(w, r) {
return true
}
h := w.Header()
path := r.URL.Path
if strings.HasSuffix(path, "/favicon.ico") {
w.Header().Set("Cache-Control", "max-age=3600")
faviconRequests.Inc()
w.Write(faviconData)
return true
}
switch r.URL.Path {
case "/health":
h.Set("Content-Type", "text/plain; charset=utf-8")
deadline := s.shutdownDelayDeadline.Load()
if deadline <= 0 {
w.Write([]byte("OK"))
return
return true
}
// Return non-OK response during grace period before shutting down the server.
// Load balancers must notify these responses and re-route new requests to other servers.
@ -353,7 +424,7 @@ func handlerWrapper(s *server, w http.ResponseWriter, r *http.Request, rh Reques
}
errMsg := fmt.Sprintf("The server is in delayed shutdown mode, which will end in %.3fs", d.Seconds())
http.Error(w, errMsg, http.StatusServiceUnavailable)
return
return true
case "/ping":
// This is needed for compatibility with InfluxDB agents.
// See https://docs.influxdata.com/influxdb/v1.7/tools/api/#ping-http-endpoint
@ -362,64 +433,54 @@ func handlerWrapper(s *server, w http.ResponseWriter, r *http.Request, rh Reques
status = http.StatusOK
}
w.WriteHeader(status)
return
return true
case "/metrics":
metricsRequests.Inc()
if !CheckAuthFlag(w, r, metricsAuthKey) {
return
return true
}
startTime := time.Now()
h.Set("Content-Type", "text/plain; charset=utf-8")
appmetrics.WritePrometheusMetrics(w)
metricsHandlerDuration.UpdateDuration(startTime)
return
return true
case "/flags":
if !CheckAuthFlag(w, r, flagsAuthKey) {
return
return true
}
h.Set("Content-Type", "text/plain; charset=utf-8")
flagutil.WriteFlags(w)
return
return true
case "/-/healthy":
// This is needed for Prometheus compatibility
// See https://github.com/VictoriaMetrics/VictoriaMetrics/issues/1833
fmt.Fprintf(w, "VictoriaMetrics is Healthy.\n")
return
return true
case "/-/ready":
// This is needed for Prometheus compatibility
// See https://github.com/VictoriaMetrics/VictoriaMetrics/issues/1833
fmt.Fprintf(w, "VictoriaMetrics is Ready.\n")
return
return true
case "/robots.txt":
// This prevents search engines from indexing contents
// See https://github.com/VictoriaMetrics/VictoriaMetrics/issues/4128
fmt.Fprintf(w, "User-agent: *\nDisallow: /\n")
return
return true
default:
if strings.HasPrefix(r.URL.Path, "/debug/pprof/") {
pprofRequests.Inc()
if !CheckAuthFlag(w, r, pprofAuthKey) {
return
return true
}
pprofHandler(r.URL.Path[len("/debug/pprof/"):], w, r)
return
return true
}
if !isProtectedByAuthFlag(r.URL.Path) && !CheckBasicAuth(w, r) {
return
return true
}
w = &responseWriterWithAbort{
ResponseWriter: w,
}
if rh(w, r) {
return
}
Errorf(w, r, "unsupported path requested: %q", r.URL.Path)
unsupportedRequestErrors.Inc()
return
}
return rh(w, r)
}
func isProtectedByAuthFlag(path string) bool {

View file

@ -162,8 +162,11 @@ func TestHandlerWrapper(t *testing.T) {
srv := &server{s: &http.Server{}}
w := &httptest.ResponseRecorder{}
handlerWrapper(srv, w, req, func(_ http.ResponseWriter, _ *http.Request) bool {
return true
handlerWrapper(w, req, func(w http.ResponseWriter, r *http.Request) bool {
return builtinRoutesHandler(srv, r, w, func(_ http.ResponseWriter, _ *http.Request) bool {
return true
})
})
h := w.Header()