mirror of
https://github.com/prometheus/prometheus.git
synced 2024-11-09 23:24:05 -08:00
Handle OPTIONS HTTP requests correctly.
Fixes https://github.com/prometheus/prometheus/issues/1346
This commit is contained in:
parent
4dc8c4f94c
commit
1ae23bf5e9
5
vendor/github.com/prometheus/common/route/route.go
generated
vendored
5
vendor/github.com/prometheus/common/route/route.go
generated
vendored
|
@ -75,6 +75,11 @@ func (r *Router) Get(path string, h http.HandlerFunc) {
|
||||||
r.rtr.GET(r.prefix+path, handle(h))
|
r.rtr.GET(r.prefix+path, handle(h))
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Options registers a new OPTIONS route.
|
||||||
|
func (r *Router) Options(path string, h http.HandlerFunc) {
|
||||||
|
r.rtr.OPTIONS(r.prefix+path, handle(h))
|
||||||
|
}
|
||||||
|
|
||||||
// Del registers a new DELETE route.
|
// Del registers a new DELETE route.
|
||||||
func (r *Router) Del(path string, h http.HandlerFunc) {
|
func (r *Router) Del(path string, h http.HandlerFunc) {
|
||||||
r.rtr.DELETE(r.prefix+path, handle(h))
|
r.rtr.DELETE(r.prefix+path, handle(h))
|
||||||
|
|
4
vendor/vendor.json
vendored
4
vendor/vendor.json
vendored
|
@ -174,8 +174,8 @@
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"path": "github.com/prometheus/common/route",
|
"path": "github.com/prometheus/common/route",
|
||||||
"revision": "4fdc91a58c9d3696b982e8a680f4997403132d44",
|
"revision": "14ca1097bbe21584194c15e391a9dab95ad42a59",
|
||||||
"revisionTime": "2015-10-26T12:04:34+01:00"
|
"revisionTime": "2016-01-25T23:57:51+01:00"
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"path": "github.com/prometheus/procfs",
|
"path": "github.com/prometheus/procfs",
|
||||||
|
|
|
@ -34,6 +34,12 @@ type API struct {
|
||||||
|
|
||||||
// Register registers the handler for the various endpoints below /api.
|
// Register registers the handler for the various endpoints below /api.
|
||||||
func (api *API) Register(router *route.Router) {
|
func (api *API) Register(router *route.Router) {
|
||||||
|
// List all the endpoints here instead of using a wildcard route because we
|
||||||
|
// would otherwise handle /api/v1 as well.
|
||||||
|
router.Options("/query", handle("options", api.Options))
|
||||||
|
router.Options("/query_range", handle("options", api.Options))
|
||||||
|
router.Options("/metrics", handle("options", api.Options))
|
||||||
|
|
||||||
router.Get("/query", handle("query", api.Query))
|
router.Get("/query", handle("query", api.Query))
|
||||||
router.Get("/query_range", handle("query_range", api.QueryRange))
|
router.Get("/query_range", handle("query_range", api.QueryRange))
|
||||||
router.Get("/metrics", handle("metrics", api.Metrics))
|
router.Get("/metrics", handle("metrics", api.Metrics))
|
||||||
|
|
|
@ -31,7 +31,7 @@ import (
|
||||||
// Enables cross-site script calls.
|
// Enables cross-site script calls.
|
||||||
func setAccessControlHeaders(w http.ResponseWriter) {
|
func setAccessControlHeaders(w http.ResponseWriter) {
|
||||||
w.Header().Set("Access-Control-Allow-Headers", "Accept, Authorization, Content-Type, Origin")
|
w.Header().Set("Access-Control-Allow-Headers", "Accept, Authorization, Content-Type, Origin")
|
||||||
w.Header().Set("Access-Control-Allow-Methods", "GET")
|
w.Header().Set("Access-Control-Allow-Methods", "GET, OPTIONS")
|
||||||
w.Header().Set("Access-Control-Allow-Origin", "*")
|
w.Header().Set("Access-Control-Allow-Origin", "*")
|
||||||
w.Header().Set("Access-Control-Expose-Headers", "Date")
|
w.Header().Set("Access-Control-Expose-Headers", "Date")
|
||||||
}
|
}
|
||||||
|
@ -61,6 +61,12 @@ func parseDuration(d string) (time.Duration, error) {
|
||||||
return time.Duration(dFloat * float64(time.Second/time.Nanosecond)), nil
|
return time.Duration(dFloat * float64(time.Second/time.Nanosecond)), nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Options handles OPTIONS requests to /api/... endpoints.
|
||||||
|
func (api *API) Options(w http.ResponseWriter, r *http.Request) {
|
||||||
|
setAccessControlHeaders(w)
|
||||||
|
w.WriteHeader(http.StatusNoContent)
|
||||||
|
}
|
||||||
|
|
||||||
// Query handles the /api/query endpoint.
|
// Query handles the /api/query endpoint.
|
||||||
func (api *API) Query(w http.ResponseWriter, r *http.Request) {
|
func (api *API) Query(w http.ResponseWriter, r *http.Request) {
|
||||||
setAccessControlHeaders(w)
|
setAccessControlHeaders(w)
|
||||||
|
|
|
@ -38,6 +38,13 @@ const (
|
||||||
errorBadData = "bad_data"
|
errorBadData = "bad_data"
|
||||||
)
|
)
|
||||||
|
|
||||||
|
var corsHeaders = map[string]string{
|
||||||
|
"Access-Control-Allow-Headers": "Accept, Authorization, Content-Type, Origin",
|
||||||
|
"Access-Control-Allow-Methods": "GET, OPTIONS",
|
||||||
|
"Access-Control-Allow-Origin": "*",
|
||||||
|
"Access-Control-Expose-Headers": "Date",
|
||||||
|
}
|
||||||
|
|
||||||
type apiError struct {
|
type apiError struct {
|
||||||
typ errorType
|
typ errorType
|
||||||
err error
|
err error
|
||||||
|
@ -56,10 +63,9 @@ type response struct {
|
||||||
|
|
||||||
// Enables cross-site script calls.
|
// Enables cross-site script calls.
|
||||||
func setCORS(w http.ResponseWriter) {
|
func setCORS(w http.ResponseWriter) {
|
||||||
w.Header().Set("Access-Control-Allow-Headers", "Accept, Authorization, Content-Type, Origin")
|
for h, v := range corsHeaders {
|
||||||
w.Header().Set("Access-Control-Allow-Methods", "GET")
|
w.Header().Set(h, v)
|
||||||
w.Header().Set("Access-Control-Allow-Origin", "*")
|
}
|
||||||
w.Header().Set("Access-Control-Expose-Headers", "Date")
|
|
||||||
}
|
}
|
||||||
|
|
||||||
type apiFunc func(r *http.Request) (interface{}, *apiError)
|
type apiFunc func(r *http.Request) (interface{}, *apiError)
|
||||||
|
@ -91,8 +97,10 @@ func (api *API) Register(r *route.Router) {
|
||||||
setCORS(w)
|
setCORS(w)
|
||||||
if data, err := f(r); err != nil {
|
if data, err := f(r); err != nil {
|
||||||
respondError(w, err, data)
|
respondError(w, err, data)
|
||||||
} else {
|
} else if data != nil {
|
||||||
respond(w, data)
|
respond(w, data)
|
||||||
|
} else {
|
||||||
|
w.WriteHeader(http.StatusNoContent)
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
return prometheus.InstrumentHandler(name, httputil.CompressionHandler{
|
return prometheus.InstrumentHandler(name, httputil.CompressionHandler{
|
||||||
|
@ -100,6 +108,8 @@ func (api *API) Register(r *route.Router) {
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
|
r.Options("/*path", instr("options", api.options))
|
||||||
|
|
||||||
r.Get("/query", instr("query", api.query))
|
r.Get("/query", instr("query", api.query))
|
||||||
r.Get("/query_range", instr("query_range", api.queryRange))
|
r.Get("/query_range", instr("query_range", api.queryRange))
|
||||||
|
|
||||||
|
@ -114,6 +124,10 @@ type queryData struct {
|
||||||
Result model.Value `json:"result"`
|
Result model.Value `json:"result"`
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (api *API) options(r *http.Request) (interface{}, *apiError) {
|
||||||
|
return nil, nil
|
||||||
|
}
|
||||||
|
|
||||||
func (api *API) query(r *http.Request) (interface{}, *apiError) {
|
func (api *API) query(r *http.Request) (interface{}, *apiError) {
|
||||||
var ts model.Time
|
var ts model.Time
|
||||||
if t := r.FormValue("time"); t != "" {
|
if t := r.FormValue("time"); t != "" {
|
||||||
|
@ -255,7 +269,7 @@ func (api *API) dropSeries(r *http.Request) (interface{}, *apiError) {
|
||||||
|
|
||||||
func respond(w http.ResponseWriter, data interface{}) {
|
func respond(w http.ResponseWriter, data interface{}) {
|
||||||
w.Header().Set("Content-Type", "application/json")
|
w.Header().Set("Content-Type", "application/json")
|
||||||
w.WriteHeader(200)
|
w.WriteHeader(http.StatusOK)
|
||||||
|
|
||||||
b, err := json.Marshal(&response{
|
b, err := json.Marshal(&response{
|
||||||
Status: statusSuccess,
|
Status: statusSuccess,
|
||||||
|
|
|
@ -503,3 +503,32 @@ func TestParseDuration(t *testing.T) {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func TestOptionsMethod(t *testing.T) {
|
||||||
|
r := route.New()
|
||||||
|
api := &API{}
|
||||||
|
api.Register(r)
|
||||||
|
|
||||||
|
s := httptest.NewServer(r)
|
||||||
|
defer s.Close()
|
||||||
|
|
||||||
|
req, err := http.NewRequest("OPTIONS", s.URL+"/any_path", nil)
|
||||||
|
if err != nil {
|
||||||
|
t.Fatalf("Error creating OPTIONS request: %s", err)
|
||||||
|
}
|
||||||
|
client := &http.Client{}
|
||||||
|
resp, err := client.Do(req)
|
||||||
|
if err != nil {
|
||||||
|
t.Fatalf("Error executing OPTIONS request: %s", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
if resp.StatusCode != http.StatusNoContent {
|
||||||
|
t.Fatalf("Expected status %d, got %d", http.StatusNoContent, resp.StatusCode)
|
||||||
|
}
|
||||||
|
|
||||||
|
for h, v := range corsHeaders {
|
||||||
|
if resp.Header.Get(h) != v {
|
||||||
|
t.Fatalf("Expected %q for header %q, got %q", v, h, resp.Header.Get(h))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
Loading…
Reference in a new issue