refactor: serve non-resident buckets with the global CORS policy

The pre-authentication CORS lookup stays resident-only, so client-supplied
path segments still cause no metadata I/O and no cache growth. The
fail-closed states for startup, load failures, and the internal namespace
are gone: CORS is a browser response policy rather than an authorization
boundary, and failing closed only denied browser clients CORS headers while
bucket metadata was still loading. A bucket whose stored CORS document does
not parse still gets no CORS headers. This removes the loadFailed bookkeeping
and the unused GetCorsConfig, HasAllowedOrigin, and generic Update path for
CORS; tests use the CORS-specific writer.

Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01PvgysXDmhPBBimCReYtA8q
Signed-off-by: Feng Ruohang <rh@vonng.com>
This commit is contained in:
Feng Ruohang
2026-09-02 14:06:56 +08:00
parent 3b5de82f5a
commit 632eb4729a
6 changed files with 42 additions and 227 deletions
+8 -12
View File
@@ -461,15 +461,15 @@ func registerAPIRouter(router *mux.Router) {
router.Methods(http.MethodPut).
HandlerFunc(s3APIMiddleware(api.PutBucketACLHandler)).
Queries("acl", "")
// GetBucketCors - this is a dummy call.
// GetBucketCors
router.Methods(http.MethodGet).
HandlerFunc(s3APIMiddleware(api.GetBucketCorsHandler)).
Queries("cors", "")
// PutBucketCors - this is a dummy call.
// PutBucketCors
router.Methods(http.MethodPut).
HandlerFunc(s3APIMiddleware(api.PutBucketCorsHandler)).
Queries("cors", "")
// DeleteBucketCors - this is a dummy call.
// DeleteBucketCors
router.Methods(http.MethodDelete).
HandlerFunc(s3APIMiddleware(api.DeleteBucketCorsHandler)).
Queries("cors", "")
@@ -787,15 +787,11 @@ func corsHandler(handler http.Handler) http.Handler {
return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
if r.Header.Get("Origin") != "" {
if bucket, _ := request2BucketObjectName(r); bucket != "" && globalBucketMetadataSys != nil {
// Resident-only lookup: this runs pre-auth for every
// Origin-bearing request using a client-supplied path segment as
// the bucket name. It must never load or cache metadata for
// arbitrary names (see GetResidentCorsConfig). GetResidentCorsConfig
// is the single decision point: it returns errInvalidArgument for
// the internal .minio.sys namespace (fail closed), a config for a
// resident bucket, errBucketMetadataNotInitialized for a real but
// unloaded bucket (fail closed), and errConfigNotFound otherwise
// (fall back to the global policy below).
// Resident-only lookup: this runs before authentication with a
// client-supplied path segment as the bucket name, so it must
// never load or cache metadata. A bucket with a stored CORS
// document that failed to parse gets no CORS headers; any other
// non-resident name falls back to the global policy below.
cfg, _, err := globalBucketMetadataSys.GetResidentCorsConfig(bucket)
if err == nil && cfg != nil {
if applyBucketCors(w, r, cfg) {