lib/httpserver: handle preflight HTTP requests properly

Previously OPTIONS HTTP requests for CORS preflight checks would trigger
the original request handler. This pull request fixes that behavior to
align with https://developer.mozilla.org/en-US/docs/Web/HTTP/Methods/OPTIONS

Fixes https://github.com/VictoriaMetrics/VictoriaMetrics/issues/5563
This commit is contained in:
andriibeee
2026-03-05 16:57:13 +02:00
committed by GitHub
parent 8f215137e7
commit 686c9a21ff
3 changed files with 62 additions and 0 deletions

View File

@@ -357,6 +357,12 @@ func handlerWrapper(w http.ResponseWriter, r *http.Request, rh RequestHandler) {
r.URL.Path = path
}
if r.Method == http.MethodOptions {
EnableCORS(w, r)
w.WriteHeader(http.StatusNoContent)
return
}
w = &responseWriterWithAbort{
ResponseWriter: w,
}
@@ -511,6 +517,8 @@ func EnableCORS(w http.ResponseWriter, _ *http.Request) {
return
}
w.Header().Set("Access-Control-Allow-Origin", "*")
w.Header().Set("Access-Control-Allow-Methods", "*")
w.Header().Set("Access-Control-Allow-Headers", "*")
}
func pprofHandler(profileName string, w http.ResponseWriter, r *http.Request) {

View File

@@ -144,6 +144,59 @@ func TestAuthKeyMetrics(t *testing.T) {
tstWithOutAuthKey("wrong", "wrong", 401)
}
func TestHandlerWrapperOptionsRequest(t *testing.T) {
handlerCalled := false
rh := func(_ http.ResponseWriter, _ *http.Request) bool {
handlerCalled = true
return true
}
f := func(t *testing.T, name string, corsDisabled bool, expectAllowOrigin bool) {
t.Helper()
handlerCalled = false
origDisableCORS := *disableCORS
*disableCORS = corsDisabled
defer func() {
*disableCORS = origDisableCORS
}()
req := httptest.NewRequest(http.MethodOptions, "/api/v1/query_range", nil)
w := httptest.NewRecorder()
handlerWrapper(w, req, rh)
res := w.Result()
_ = res.Body.Close()
if res.StatusCode != http.StatusNoContent {
t.Fatalf("%s: unexpected status code; got %d; want %d", name, res.StatusCode, http.StatusNoContent)
}
if handlerCalled {
t.Fatalf("%s: request handler must not be called for OPTIONS requests", name)
}
if got := res.Header.Get("Access-Control-Allow-Methods"); got != "*" {
t.Fatalf("%s: unexpected Access-Control-Allow-Methods; got %q; want %q", name, got, "*")
}
wantHeaders := "*"
if got := res.Header.Get("Access-Control-Allow-Headers"); got != wantHeaders {
t.Fatalf("%s: unexpected Access-Control-Allow-Headers; got %q; want %q", name, got, wantHeaders)
}
if expectAllowOrigin {
if got := res.Header.Get("Access-Control-Allow-Origin"); got != "*" {
t.Fatalf("%s: unexpected Access-Control-Allow-Origin; got %q; want %q", name, got, "*")
}
} else {
if got := res.Header.Get("Access-Control-Allow-Origin"); got != "" {
t.Fatalf("%s: Access-Control-Allow-Origin must be empty when CORS is disabled; got %q", name, got)
}
}
}
f(t, "cors enabled", false, true)
f(t, "cors disabled", true, false)
}
func TestHandlerWrapper(t *testing.T) {
const hstsHeader = "foo"
const frameOptionsHeader = "bar"