mirror of
https://github.com/VictoriaMetrics/VictoriaMetrics.git
synced 2026-05-17 00:26:36 +03:00
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:
@@ -26,6 +26,7 @@ See also [LTS releases](https://docs.victoriametrics.com/victoriametrics/lts-rel
|
||||
|
||||
## tip
|
||||
|
||||
* FEATURE: all VictoriaMetrics components: implement proper CORS preflight handling by responding 204 No Content to HTTP OPTIONS requests. See [#5563](https://github.com/VictoriaMetrics/VictoriaMetrics/issues/5563).
|
||||
* FEATURE: [vmauth](https://docs.victoriametrics.com/victoriametrics/vmauth/): add `access_log` configuration option for each user that will log requests to stdout, and support filtering by HTTP status codes. See more in [docs](https://docs.victoriametrics.com/victoriametrics/vmauth/#access-log). See [#5936](https://github.com/VictoriaMetrics/VictoriaMetrics/issues/5936).
|
||||
* FEATURE: [vmalert](https://docs.victoriametrics.com/victoriametrics/vmalert/): support negative values for the group `eval_offset` option, which allows starting group evaluation at `groupInterval-abs(eval_offset)` within `[0...groupInterval]`. See [#10424](https://github.com/VictoriaMetrics/VictoriaMetrics/issues/10424).
|
||||
* FEATURE: [vmsingle](https://docs.victoriametrics.com/victoriametrics/single-server-victoriametrics/) and `vmselect` in [VictoriaMetrics cluster](https://docs.victoriametrics.com/victoriametrics/cluster-victoriametrics/): Disable `/graphite/tags/tagSeries` and `/graphite/tags/tagMultiSeries` for Graphite tag registration since it is unlikely it is used in context of VictoriaMetrics. See [10544](https://github.com/VictoriaMetrics/VictoriaMetrics/issues/10544).
|
||||
|
||||
@@ -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) {
|
||||
|
||||
@@ -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"
|
||||
|
||||
Reference in New Issue
Block a user