aboutsummaryrefslogtreecommitdiff
path: root/weed/s3api/cors/middleware.go
blob: 14ff32355b4ccdc6270dfd5909dd8de5dd2cfe01 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
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)
}