diff --git a/lib/httpserver/httpserver.go b/lib/httpserver/httpserver.go index 8f439bbe96..37467132a5 100644 --- a/lib/httpserver/httpserver.go +++ b/lib/httpserver/httpserver.go @@ -27,7 +27,9 @@ var ( tlsCertFile = flag.String("tlsCertFile", "", "Path to file with TLS certificate. Used only if -tls is set. Prefer ECDSA certs instead of RSA certs, since RSA certs are slow") tlsKeyFile = flag.String("tlsKeyFile", "", "Path to file with TLS key. Used only if -tls is set") - httpExternalURL = flag.String("http.externalURL", "", "The URL under which the http service is externally reachable") + 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, "+ + "then all the http requests will be handled on '/foo/bar/*' paths. This may be useful for proxied requests. "+ + "See https://www.robustperception.io/using-external-urls-and-proxies-with-prometheus") httpAuthUsername = flag.String("httpAuth.username", "", "Username for HTTP Basic Auth. The authentication is disabled if empty. See also -httpAuth.password") httpAuthPassword = flag.String("httpAuth.password", "", "Password for HTTP Basic Auth. The authentication is disabled if -httpAuth.username is empty") metricsAuthKey = flag.String("metricsAuthKey", "", "Auth key for /metrics. It overrides httpAuth settings") @@ -63,15 +65,6 @@ func Serve(addr string, rh RequestHandler) { if *tlsEnable { scheme = "https" } - // parse and format `httpExternalURL` to /<defined_string>` , - if *httpExternalURL != "" { - if !strings.HasPrefix(*httpExternalURL, "/") { - *httpExternalURL = "/" + *httpExternalURL - } - if strings.HasSuffix(*httpExternalURL, "/") { - *httpExternalURL = strings.TrimRight(*httpExternalURL, "/") - } - } logger.Infof("starting http server at %s://%s/", scheme, addr) logger.Infof("pprof handlers are exposed at %s://%s/debug/pprof/", scheme, addr) lnTmp, err := netutil.NewTCPListener(scheme, addr) @@ -167,8 +160,13 @@ var metricsHandlerDuration = metrics.NewHistogram(`vm_http_request_duration_seco func handlerWrapper(w http.ResponseWriter, r *http.Request, rh RequestHandler) { requestsTotal.Inc() - // delete extra url string, extra is a string formated as `/<defined_string>` when server start up - r.URL.Path = strings.Replace(r.URL.Path, *httpExternalURL, "", 1) + path, err := getCanonicalPath(r.URL.Path) + if err != nil { + Errorf(w, "cannot get canonical path: %s", err) + unsupportedRequestErrors.Inc() + return + } + r.URL.Path = path switch r.URL.Path { case "/health": w.Header().Set("Content-Type", "text/plain") @@ -223,6 +221,21 @@ func handlerWrapper(w http.ResponseWriter, r *http.Request, rh RequestHandler) { } } +func getCanonicalPath(path string) (string, error) { + if len(*pathPrefix) == 0 { + return path, nil + } + prefix := *pathPrefix + if !strings.HasSuffix(prefix, "/") { + prefix = prefix + "/" + } + if !strings.HasPrefix(path, prefix) { + return "", fmt.Errorf("missing `-pathPrefix=%q` in the requested path: %q", *pathPrefix, path) + } + path = path[len(prefix)-1:] + return path, nil +} + func checkBasicAuth(w http.ResponseWriter, r *http.Request) bool { if len(*httpAuthUsername) == 0 { // HTTP Basic Auth is disabled.