Handle OPTIONS HTTP requests correctly.
Fixes https://github.com/prometheus/prometheus/issues/1346
This commit is contained in:
parent
4dc8c4f94c
commit
1ae23bf5e9
|
@ -75,6 +75,11 @@ func (r *Router) Get(path string, h http.HandlerFunc) {
|
|||
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.
|
||||
func (r *Router) Del(path string, h http.HandlerFunc) {
|
||||
r.rtr.DELETE(r.prefix+path, handle(h))
|
||||
|
|
|
@ -174,8 +174,8 @@
|
|||
},
|
||||
{
|
||||
"path": "github.com/prometheus/common/route",
|
||||
"revision": "4fdc91a58c9d3696b982e8a680f4997403132d44",
|
||||
"revisionTime": "2015-10-26T12:04:34+01:00"
|
||||
"revision": "14ca1097bbe21584194c15e391a9dab95ad42a59",
|
||||
"revisionTime": "2016-01-25T23:57:51+01:00"
|
||||
},
|
||||
{
|
||||
"path": "github.com/prometheus/procfs",
|
||||
|
|
|
@ -34,6 +34,12 @@ type API struct {
|
|||
|
||||
// Register registers the handler for the various endpoints below /api.
|
||||
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_range", handle("query_range", api.QueryRange))
|
||||
router.Get("/metrics", handle("metrics", api.Metrics))
|
||||
|
|
|
@ -31,7 +31,7 @@ import (
|
|||
// Enables cross-site script calls.
|
||||
func setAccessControlHeaders(w http.ResponseWriter) {
|
||||
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-Expose-Headers", "Date")
|
||||
}
|
||||
|
@ -61,6 +61,12 @@ func parseDuration(d string) (time.Duration, error) {
|
|||
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.
|
||||
func (api *API) Query(w http.ResponseWriter, r *http.Request) {
|
||||
setAccessControlHeaders(w)
|
||||
|
|
|
@ -38,6 +38,13 @@ const (
|
|||
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 {
|
||||
typ errorType
|
||||
err error
|
||||
|
@ -56,10 +63,9 @@ type response struct {
|
|||
|
||||
// Enables cross-site script calls.
|
||||
func setCORS(w http.ResponseWriter) {
|
||||
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-Origin", "*")
|
||||
w.Header().Set("Access-Control-Expose-Headers", "Date")
|
||||
for h, v := range corsHeaders {
|
||||
w.Header().Set(h, v)
|
||||
}
|
||||
}
|
||||
|
||||
type apiFunc func(r *http.Request) (interface{}, *apiError)
|
||||
|
@ -91,8 +97,10 @@ func (api *API) Register(r *route.Router) {
|
|||
setCORS(w)
|
||||
if data, err := f(r); err != nil {
|
||||
respondError(w, err, data)
|
||||
} else {
|
||||
} else if data != nil {
|
||||
respond(w, data)
|
||||
} else {
|
||||
w.WriteHeader(http.StatusNoContent)
|
||||
}
|
||||
})
|
||||
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_range", instr("query_range", api.queryRange))
|
||||
|
||||
|
@ -114,6 +124,10 @@ type queryData struct {
|
|||
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) {
|
||||
var ts model.Time
|
||||
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{}) {
|
||||
w.Header().Set("Content-Type", "application/json")
|
||||
w.WriteHeader(200)
|
||||
w.WriteHeader(http.StatusOK)
|
||||
|
||||
b, err := json.Marshal(&response{
|
||||
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 New Issue