web: simplify prefix handling using util/route package.

This commit is contained in:
Fabian Reinartz 2015-06-03 08:38:50 +02:00
parent 6c24114a7b
commit e88e5f680b
6 changed files with 59 additions and 72 deletions

View File

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

View File

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

View File

@ -25,6 +25,7 @@ import (
"github.com/prometheus/prometheus/promql"
"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
@ -97,9 +98,10 @@ func TestQuery(t *testing.T) {
Storage: 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()
for i, s := range scenarios {

View File

@ -9,6 +9,8 @@ import (
"strings"
"github.com/prometheus/log"
"github.com/prometheus/prometheus/util/route"
)
// Sub-directories for templates and static content.
@ -46,7 +48,9 @@ func GetFile(bucket string, name string) ([]byte, error) {
type Handler struct{}
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 == "" {
name = "index.html"
}

View File

@ -22,8 +22,10 @@ import (
"path/filepath"
clientmodel "github.com/prometheus/client_golang/model"
"github.com/prometheus/prometheus/promql"
"github.com/prometheus/prometheus/template"
"github.com/prometheus/prometheus/util/route"
)
var (
@ -38,7 +40,10 @@ type ConsolesHandler struct {
}
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 {
http.Error(w, err.Error(), http.StatusNotFound)
return
@ -67,10 +72,10 @@ func (h *ConsolesHandler) ServeHTTP(w http.ResponseWriter, r *http.Request) {
}{
RawParams: rawParams,
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")
if err != nil {
http.Error(w, err.Error(), http.StatusInternalServerError)

View File

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