aboutsummaryrefslogtreecommitdiff
path: root/weed/s3api/cors/middleware.go
diff options
context:
space:
mode:
authorChris Lu <chrislusf@users.noreply.github.com>2025-07-15 00:23:54 -0700
committerGitHub <noreply@github.com>2025-07-15 00:23:54 -0700
commit4b040e8a8701199d4c680bb6f241c4751c8210a2 (patch)
tree45d76546220c8d6f3287e3f5498ddf598079cc8e /weed/s3api/cors/middleware.go
parent548fa0b50a2a57de538d6f6961bfe819128d0ee5 (diff)
downloadseaweedfs-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/cors/middleware.go')
-rw-r--r--weed/s3api/cors/middleware.go143
1 files changed, 143 insertions, 0 deletions
diff --git a/weed/s3api/cors/middleware.go b/weed/s3api/cors/middleware.go
new file mode 100644
index 000000000..14ff32355
--- /dev/null
+++ b/weed/s3api/cors/middleware.go
@@ -0,0 +1,143 @@
+package cors
+
+import (
+ "net/http"
+
+ "github.com/seaweedfs/seaweedfs/weed/glog"
+ "github.com/seaweedfs/seaweedfs/weed/s3api/s3_constants"
+ "github.com/seaweedfs/seaweedfs/weed/s3api/s3err"
+)
+
+// BucketChecker interface for checking bucket existence
+type BucketChecker interface {
+ CheckBucket(r *http.Request, bucket string) s3err.ErrorCode
+}
+
+// CORSConfigGetter interface for getting CORS configuration
+type CORSConfigGetter interface {
+ GetCORSConfiguration(bucket string) (*CORSConfiguration, s3err.ErrorCode)
+}
+
+// Middleware handles CORS evaluation for all S3 API requests
+type Middleware struct {
+ storage *Storage
+ bucketChecker BucketChecker
+ corsConfigGetter CORSConfigGetter
+}
+
+// NewMiddleware creates a new CORS middleware instance
+func NewMiddleware(storage *Storage, bucketChecker BucketChecker, corsConfigGetter CORSConfigGetter) *Middleware {
+ return &Middleware{
+ storage: storage,
+ bucketChecker: bucketChecker,
+ corsConfigGetter: corsConfigGetter,
+ }
+}
+
+// evaluateCORSRequest performs the common CORS request evaluation logic
+// Returns: (corsResponse, responseWritten, shouldContinue)
+// - corsResponse: the CORS response if evaluation succeeded
+// - responseWritten: true if an error response was already written
+// - shouldContinue: true if the request should continue to the next handler
+func (m *Middleware) evaluateCORSRequest(w http.ResponseWriter, r *http.Request) (*CORSResponse, bool, bool) {
+ // Parse CORS request
+ corsReq := ParseRequest(r)
+ if corsReq.Origin == "" {
+ // Not a CORS request
+ return nil, false, true
+ }
+
+ // Extract bucket from request
+ bucket, _ := s3_constants.GetBucketAndObject(r)
+ if bucket == "" {
+ return nil, false, true
+ }
+
+ // Check if bucket exists
+ if err := m.bucketChecker.CheckBucket(r, bucket); err != s3err.ErrNone {
+ // For non-existent buckets, let the normal handler deal with it
+ return nil, false, true
+ }
+
+ // Load CORS configuration from cache
+ config, errCode := m.corsConfigGetter.GetCORSConfiguration(bucket)
+ if errCode != s3err.ErrNone || config == nil {
+ // No CORS configuration, handle based on request type
+ if corsReq.IsPreflightRequest {
+ // Preflight request without CORS config should fail
+ s3err.WriteErrorResponse(w, r, s3err.ErrAccessDenied)
+ return nil, true, false // Response written, don't continue
+ }
+ // Non-preflight request, continue normally
+ return nil, false, true
+ }
+
+ // Evaluate CORS request
+ corsResp, err := EvaluateRequest(config, corsReq)
+ if err != nil {
+ glog.V(3).Infof("CORS evaluation failed for bucket %s: %v", bucket, err)
+ if corsReq.IsPreflightRequest {
+ // Preflight request that doesn't match CORS rules should fail
+ s3err.WriteErrorResponse(w, r, s3err.ErrAccessDenied)
+ return nil, true, false // Response written, don't continue
+ }
+ // Non-preflight request, continue normally but without CORS headers
+ return nil, false, true
+ }
+
+ return corsResp, false, false
+}
+
+// Handler returns the CORS middleware handler
+func (m *Middleware) Handler(next http.Handler) http.Handler {
+ return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
+ // Use the common evaluation logic
+ corsResp, responseWritten, shouldContinue := m.evaluateCORSRequest(w, r)
+ if responseWritten {
+ // Response was already written (error case)
+ return
+ }
+
+ if shouldContinue {
+ // Continue with normal request processing
+ next.ServeHTTP(w, r)
+ return
+ }
+
+ // Parse request to check if it's a preflight request
+ corsReq := ParseRequest(r)
+
+ // Apply CORS headers to response
+ ApplyHeaders(w, corsResp)
+
+ // Handle preflight requests
+ if corsReq.IsPreflightRequest {
+ // Preflight request should return 200 OK with just CORS headers
+ w.WriteHeader(http.StatusOK)
+ return
+ }
+
+ // Continue with normal request processing
+ next.ServeHTTP(w, r)
+ })
+}
+
+// HandleOptionsRequest handles OPTIONS requests for CORS preflight
+func (m *Middleware) HandleOptionsRequest(w http.ResponseWriter, r *http.Request) {
+ // Use the common evaluation logic
+ corsResp, responseWritten, shouldContinue := m.evaluateCORSRequest(w, r)
+ if responseWritten {
+ // Response was already written (error case)
+ return
+ }
+
+ if shouldContinue || corsResp == nil {
+ // Not a CORS request or should continue normally
+ w.WriteHeader(http.StatusOK)
+ return
+ }
+
+ // Apply CORS headers and return success
+ ApplyHeaders(w, corsResp)
+ w.WriteHeader(http.StatusOK)
+}