diff options
| author | Chris Lu <chrislusf@users.noreply.github.com> | 2025-07-15 00:23:54 -0700 |
|---|---|---|
| committer | GitHub <noreply@github.com> | 2025-07-15 00:23:54 -0700 |
| commit | 4b040e8a8701199d4c680bb6f241c4751c8210a2 (patch) | |
| tree | 45d76546220c8d6f3287e3f5498ddf598079cc8e /weed/s3api/s3api_server.go | |
| parent | 548fa0b50a2a57de538d6f6961bfe819128d0ee5 (diff) | |
| download | seaweedfs-4b040e8a8701199d4c680bb6f241c4751c8210a2.tar.xz seaweedfs-4b040e8a8701199d4c680bb6f241c4751c8210a2.zip | |
adding cors support (#6987)
* adding cors support
* address some comments
* optimize matchesWildcard
* address comments
* fix for tests
* address comments
* address comments
* address comments
* path building
* refactor
* Update weed/s3api/s3api_bucket_config.go
Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com>
* address comment
Service-level responses need both Access-Control-Allow-Methods and Access-Control-Allow-Headers. After setting Access-Control-Allow-Origin and Access-Control-Expose-Headers, also set Access-Control-Allow-Methods: * and Access-Control-Allow-Headers: * so service endpoints satisfy CORS preflight requirements.
* Update weed/s3api/s3api_bucket_config.go
Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com>
* Update weed/s3api/s3api_object_handlers.go
Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com>
* Update weed/s3api/s3api_object_handlers.go
Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com>
* fix
* refactor
* Update weed/s3api/s3api_bucket_config.go
Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com>
* Update weed/s3api/s3api_object_handlers.go
Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com>
* Update weed/s3api/s3api_server.go
Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com>
* simplify
* add cors tests
* fix tests
* fix tests
---------
Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com>
Diffstat (limited to 'weed/s3api/s3api_server.go')
| -rw-r--r-- | weed/s3api/s3api_server.go | 84 |
1 files changed, 57 insertions, 27 deletions
diff --git a/weed/s3api/s3api_server.go b/weed/s3api/s3api_server.go index 426535fe0..5d113c645 100644 --- a/weed/s3api/s3api_server.go +++ b/weed/s3api/s3api_server.go @@ -121,6 +121,35 @@ func NewS3ApiServerWithStore(router *mux.Router, option *S3ApiServerOption, expl return s3ApiServer, nil } +// handleCORSOriginValidation handles the common CORS origin validation logic +func (s3a *S3ApiServer) handleCORSOriginValidation(w http.ResponseWriter, r *http.Request) bool { + origin := r.Header.Get("Origin") + if origin != "" { + if len(s3a.option.AllowedOrigins) == 0 || s3a.option.AllowedOrigins[0] == "*" { + origin = "*" + } else { + originFound := false + for _, allowedOrigin := range s3a.option.AllowedOrigins { + if origin == allowedOrigin { + originFound = true + break + } + } + if !originFound { + writeFailureResponse(w, r, http.StatusForbidden) + return false + } + } + } + + w.Header().Set("Access-Control-Allow-Origin", origin) + w.Header().Set("Access-Control-Expose-Headers", "*") + w.Header().Set("Access-Control-Allow-Methods", "*") + w.Header().Set("Access-Control-Allow-Headers", "*") + w.Header().Set("Access-Control-Allow-Credentials", "true") + return true +} + func (s3a *S3ApiServer) registerRouter(router *mux.Router) { // API Router apiRouter := router.PathPrefix("/").Subrouter() @@ -129,33 +158,6 @@ func (s3a *S3ApiServer) registerRouter(router *mux.Router) { apiRouter.Methods(http.MethodGet).Path("/status").HandlerFunc(s3a.StatusHandler) apiRouter.Methods(http.MethodGet).Path("/healthz").HandlerFunc(s3a.StatusHandler) - apiRouter.Methods(http.MethodOptions).HandlerFunc( - func(w http.ResponseWriter, r *http.Request) { - origin := r.Header.Get("Origin") - if origin != "" { - if len(s3a.option.AllowedOrigins) == 0 || s3a.option.AllowedOrigins[0] == "*" { - origin = "*" - } else { - originFound := false - for _, allowedOrigin := range s3a.option.AllowedOrigins { - if origin == allowedOrigin { - originFound = true - } - } - if !originFound { - writeFailureResponse(w, r, http.StatusForbidden) - return - } - } - } - - w.Header().Set("Access-Control-Allow-Origin", origin) - w.Header().Set("Access-Control-Expose-Headers", "*") - w.Header().Set("Access-Control-Allow-Methods", "*") - w.Header().Set("Access-Control-Allow-Headers", "*") - writeSuccessResponseEmpty(w, r) - }) - var routers []*mux.Router if s3a.option.DomainName != "" { domainNames := strings.Split(s3a.option.DomainName, ",") @@ -168,7 +170,16 @@ func (s3a *S3ApiServer) registerRouter(router *mux.Router) { } routers = append(routers, apiRouter.PathPrefix("/{bucket}").Subrouter()) + // Get CORS middleware instance with caching + corsMiddleware := s3a.getCORSMiddleware() + for _, bucket := range routers { + // Apply CORS middleware to bucket routers for automatic CORS header handling + bucket.Use(corsMiddleware.Handler) + + // Bucket-specific OPTIONS handler for CORS preflight requests + // Use PathPrefix to catch all bucket-level preflight routes including /bucket/object + bucket.PathPrefix("/").Methods(http.MethodOptions).HandlerFunc(corsMiddleware.HandleOptionsRequest) // each case should follow the next rule: // - requesting object with query must precede any other methods @@ -330,6 +341,25 @@ func (s3a *S3ApiServer) registerRouter(router *mux.Router) { } + // Global OPTIONS handler for service-level requests (non-bucket requests) + // This handles requests like OPTIONS /, OPTIONS /status, OPTIONS /healthz + // Place this after bucket handlers to avoid interfering with bucket CORS middleware + apiRouter.Methods(http.MethodOptions).PathPrefix("/").HandlerFunc( + func(w http.ResponseWriter, r *http.Request) { + // Only handle if this is not a bucket-specific request + vars := mux.Vars(r) + bucket := vars["bucket"] + if bucket != "" { + // This is a bucket-specific request, let bucket CORS middleware handle it + http.NotFound(w, r) + return + } + + if s3a.handleCORSOriginValidation(w, r) { + writeSuccessResponseEmpty(w, r) + } + }) + // ListBuckets apiRouter.Methods(http.MethodGet).Path("/").HandlerFunc(track(s3a.ListBucketsHandler, "LIST")) |
