web: simplify prefix handling using util/route package.

pull/764/head
Fabian Reinartz 10 years ago
parent 6c24114a7b
commit e88e5f680b

@ -60,8 +60,7 @@ func New() *Router {
return &Router{rtr: httprouter.New()} return &Router{rtr: httprouter.New()}
} }
// WithPrefix returns a router that prefixes all registered routes // WithPrefix returns a router that prefixes all registered routes with prefix.
// with preifx.
func (r *Router) WithPrefix(prefix string) *Router { func (r *Router) WithPrefix(prefix string) *Router {
return &Router{rtr: r.rtr, prefix: r.prefix + prefix} return &Router{rtr: r.rtr, prefix: r.prefix + prefix}
} }

@ -23,6 +23,7 @@ import (
"github.com/prometheus/prometheus/promql" "github.com/prometheus/prometheus/promql"
"github.com/prometheus/prometheus/storage/local" "github.com/prometheus/prometheus/storage/local"
"github.com/prometheus/prometheus/util/httputil" "github.com/prometheus/prometheus/util/httputil"
"github.com/prometheus/prometheus/util/route"
) )
// MetricsService manages the /api HTTP endpoint. // MetricsService manages the /api HTTP endpoint.
@ -33,19 +34,15 @@ type MetricsService struct {
} }
// RegisterHandler registers the handler for the various endpoints below /api. // RegisterHandler registers the handler for the various endpoints below /api.
func (msrv *MetricsService) RegisterHandler(mux *http.ServeMux, pathPrefix string) { func (msrv *MetricsService) RegisterHandler(router *route.Router) {
handler := func(h func(http.ResponseWriter, *http.Request)) http.Handler { router.Get("/query", handle("query", msrv.Query))
return httputil.CompressionHandler{ router.Get("/query_range", handle("query_range", msrv.QueryRange))
Handler: http.HandlerFunc(h), router.Get("/metrics", handle("metrics", msrv.Metrics))
} }
func handle(name string, f http.HandlerFunc) http.HandlerFunc {
h := httputil.CompressionHandler{
Handler: f,
} }
mux.Handle(pathPrefix+"/api/query", prometheus.InstrumentHandler( return prometheus.InstrumentHandler(name, h)
pathPrefix+"/api/query", handler(msrv.Query),
))
mux.Handle(pathPrefix+"/api/query_range", prometheus.InstrumentHandler(
pathPrefix+"/api/query_range", handler(msrv.QueryRange),
))
mux.Handle(pathPrefix+"/api/metrics", prometheus.InstrumentHandler(
pathPrefix+"/api/metrics", handler(msrv.Metrics),
))
} }

@ -25,6 +25,7 @@ import (
"github.com/prometheus/prometheus/promql" "github.com/prometheus/prometheus/promql"
"github.com/prometheus/prometheus/storage/local" "github.com/prometheus/prometheus/storage/local"
"github.com/prometheus/prometheus/util/route"
) )
// This is a bit annoying. On one hand, we have to choose a current timestamp // This is a bit annoying. On one hand, we have to choose a current timestamp
@ -97,9 +98,10 @@ func TestQuery(t *testing.T) {
Storage: storage, Storage: storage,
QueryEngine: promql.NewEngine(storage), QueryEngine: promql.NewEngine(storage),
} }
api.RegisterHandler(http.DefaultServeMux, "") rtr := route.New()
api.RegisterHandler(rtr.WithPrefix("/api"))
server := httptest.NewServer(http.DefaultServeMux) server := httptest.NewServer(rtr)
defer server.Close() defer server.Close()
for i, s := range scenarios { for i, s := range scenarios {

@ -9,6 +9,8 @@ import (
"strings" "strings"
"github.com/prometheus/log" "github.com/prometheus/log"
"github.com/prometheus/prometheus/util/route"
) )
// Sub-directories for templates and static content. // Sub-directories for templates and static content.
@ -46,7 +48,9 @@ func GetFile(bucket string, name string) ([]byte, error) {
type Handler struct{} type Handler struct{}
func (h Handler) ServeHTTP(w http.ResponseWriter, r *http.Request) { func (h Handler) ServeHTTP(w http.ResponseWriter, r *http.Request) {
name := r.URL.Path ctx := route.Context(r)
name := strings.Trim(route.Param(ctx, "filepath"), "/")
if name == "" { if name == "" {
name = "index.html" name = "index.html"
} }

@ -22,8 +22,10 @@ import (
"path/filepath" "path/filepath"
clientmodel "github.com/prometheus/client_golang/model" clientmodel "github.com/prometheus/client_golang/model"
"github.com/prometheus/prometheus/promql" "github.com/prometheus/prometheus/promql"
"github.com/prometheus/prometheus/template" "github.com/prometheus/prometheus/template"
"github.com/prometheus/prometheus/util/route"
) )
var ( var (
@ -38,7 +40,10 @@ type ConsolesHandler struct {
} }
func (h *ConsolesHandler) ServeHTTP(w http.ResponseWriter, r *http.Request) { func (h *ConsolesHandler) ServeHTTP(w http.ResponseWriter, r *http.Request) {
file, err := http.Dir(*consoleTemplatesPath).Open(r.URL.Path) ctx := route.Context(r)
name := route.Param(ctx, "filepath")
file, err := http.Dir(*consoleTemplatesPath).Open(name)
if err != nil { if err != nil {
http.Error(w, err.Error(), http.StatusNotFound) http.Error(w, err.Error(), http.StatusNotFound)
return return
@ -67,10 +72,10 @@ func (h *ConsolesHandler) ServeHTTP(w http.ResponseWriter, r *http.Request) {
}{ }{
RawParams: rawParams, RawParams: rawParams,
Params: params, Params: params,
Path: r.URL.Path, Path: name,
} }
tmpl := template.NewTemplateExpander(string(text), "__console_"+r.URL.Path, data, clientmodel.Now(), h.QueryEngine, h.PathPrefix) tmpl := template.NewTemplateExpander(string(text), "__console_"+name, data, clientmodel.Now(), h.QueryEngine, h.PathPrefix)
filenames, err := filepath.Glob(*consoleLibrariesPath + "/*.lib") filenames, err := filepath.Glob(*consoleLibrariesPath + "/*.lib")
if err != nil { if err != nil {
http.Error(w, err.Error(), http.StatusInternalServerError) http.Error(w, err.Error(), http.StatusInternalServerError)

@ -28,6 +28,7 @@ import (
"github.com/prometheus/client_golang/prometheus" "github.com/prometheus/client_golang/prometheus"
"github.com/prometheus/log" "github.com/prometheus/log"
"github.com/prometheus/prometheus/util/route"
clientmodel "github.com/prometheus/client_golang/model" clientmodel "github.com/prometheus/client_golang/model"
@ -50,7 +51,7 @@ var (
// WebService handles the HTTP endpoints with the exception of /api. // WebService handles the HTTP endpoints with the exception of /api.
type WebService struct { type WebService struct {
QuitChan chan struct{} QuitChan chan struct{}
mux *http.ServeMux router *route.Router
} }
type WebServiceOptions struct { type WebServiceOptions struct {
@ -64,61 +65,46 @@ type WebServiceOptions struct {
// NewWebService returns a new WebService. // NewWebService returns a new WebService.
func NewWebService(o *WebServiceOptions) *WebService { func NewWebService(o *WebServiceOptions) *WebService {
mux := http.NewServeMux() router := route.New()
ws := &WebService{ ws := &WebService{
mux: mux, router: router,
QuitChan: make(chan struct{}), QuitChan: make(chan struct{}),
} }
mux.HandleFunc("/", prometheus.InstrumentHandlerFunc(o.PathPrefix, func(rw http.ResponseWriter, req *http.Request) { if o.PathPrefix != "" {
// The "/" pattern matches everything, so we need to check // If the prefix is missing for the root path, append it.
// that we're at the root here. router.Get("/", func(w http.ResponseWriter, r *http.Request) {
if req.URL.Path == o.PathPrefix+"/" { http.Redirect(w, r, o.PathPrefix, 301)
o.StatusHandler.ServeHTTP(rw, req) })
} else if req.URL.Path == o.PathPrefix { router = router.WithPrefix(o.PathPrefix)
http.Redirect(rw, req, o.PathPrefix+"/", http.StatusFound) }
} else if !strings.HasPrefix(req.URL.Path, o.PathPrefix+"/") {
// We're running under a prefix but the user requested something instr := prometheus.InstrumentHandler
// outside of it. Let's see if this page exists under the prefix.
http.Redirect(rw, req, o.PathPrefix+req.URL.Path, http.StatusFound) router.Get("/", instr("status", o.StatusHandler))
} else { router.Get("/alerts", instr("alerts", o.AlertsHandler))
http.NotFound(rw, req) router.Get("/graph", instr("graph", o.GraphsHandler))
} router.Get("/heap", instr("heap", http.HandlerFunc(dumpHeap)))
}))
mux.Handle(o.PathPrefix+"/alerts", prometheus.InstrumentHandler( router.Get(*metricsPath, prometheus.Handler().ServeHTTP)
o.PathPrefix+"/alerts", o.AlertsHandler,
)) o.MetricsHandler.RegisterHandler(router.WithPrefix("/api"))
mux.Handle(o.PathPrefix+"/consoles/", prometheus.InstrumentHandler(
o.PathPrefix+"/consoles/", http.StripPrefix(o.PathPrefix+"/consoles/", o.ConsolesHandler), router.Get("/consoles/*filepath", instr("consoles", o.ConsolesHandler))
))
mux.Handle(o.PathPrefix+"/graph", prometheus.InstrumentHandler(
o.PathPrefix+"/graph", o.GraphsHandler,
))
mux.Handle(o.PathPrefix+"/heap", prometheus.InstrumentHandler(
o.PathPrefix+"/heap", http.HandlerFunc(dumpHeap),
))
o.MetricsHandler.RegisterHandler(mux, o.PathPrefix)
mux.Handle(o.PathPrefix+*metricsPath, prometheus.Handler())
if *useLocalAssets { if *useLocalAssets {
mux.Handle(o.PathPrefix+"/static/", prometheus.InstrumentHandler( router.Get("/static/*filepath", instr("static", route.FileServe("web/static")))
o.PathPrefix+"/static/", http.StripPrefix(o.PathPrefix+"/static/", http.FileServer(http.Dir("web/static"))),
))
} else { } else {
mux.Handle(o.PathPrefix+"/static/", prometheus.InstrumentHandler( router.Get("/static/*filepath", instr("static", blob.Handler{}))
o.PathPrefix+"/static/", http.StripPrefix(o.PathPrefix+"/static/", new(blob.Handler)),
))
} }
if *userAssetsPath != "" { if *userAssetsPath != "" {
mux.Handle(o.PathPrefix+"/user/", prometheus.InstrumentHandler( router.Get("/user/*filepath", instr("user", route.FileServe(*userAssetsPath)))
o.PathPrefix+"/user/", http.StripPrefix(o.PathPrefix+"/user/", http.FileServer(http.Dir(*userAssetsPath))),
))
} }
if *enableQuit { if *enableQuit {
mux.Handle(o.PathPrefix+"/-/quit", http.HandlerFunc(ws.quitHandler)) router.Post("/-/quit", ws.quitHandler)
} }
return ws return ws
@ -130,7 +116,7 @@ func (ws *WebService) Run() {
// If we cannot bind to a port, retry after 30 seconds. // If we cannot bind to a port, retry after 30 seconds.
for { for {
err := http.ListenAndServe(*listenAddress, ws.mux) err := http.ListenAndServe(*listenAddress, ws.router)
if err != nil { if err != nil {
log.Errorf("Could not listen on %s: %s", *listenAddress, err) log.Errorf("Could not listen on %s: %s", *listenAddress, err)
} }
@ -139,12 +125,6 @@ func (ws *WebService) Run() {
} }
func (ws *WebService) quitHandler(w http.ResponseWriter, r *http.Request) { func (ws *WebService) quitHandler(w http.ResponseWriter, r *http.Request) {
if r.Method != "POST" {
w.Header().Add("Allow", "POST")
w.WriteHeader(http.StatusMethodNotAllowed)
return
}
fmt.Fprintf(w, "Requesting termination... Goodbye!") fmt.Fprintf(w, "Requesting termination... Goodbye!")
close(ws.QuitChan) close(ws.QuitChan)

Loading…
Cancel
Save