aboutsummaryrefslogtreecommitdiff
path: root/weed/s3api
diff options
context:
space:
mode:
authorChris Lu <chrislusf@users.noreply.github.com>2025-11-26 17:17:02 -0800
committerGitHub <noreply@github.com>2025-11-26 17:17:02 -0800
commitebb4f57cc73580deccfcce39cb8cd5af9682206c (patch)
treec474acb116ac4d5c818975da5ee190e893df225d /weed/s3api
parent4106fc0436ce91591820a59a9715a06cacf06724 (diff)
downloadseaweedfs-ebb4f57cc73580deccfcce39cb8cd5af9682206c.tar.xz
seaweedfs-ebb4f57cc73580deccfcce39cb8cd5af9682206c.zip
s3api: Fix response-content-disposition query parameter not being honored (#7559)
* s3api: Fix response-content-disposition query parameter not being honored Fixes #7486 This fix resolves an issue where S3 presigned URLs with query parameters like `response-content-disposition`, `response-content-type`, etc. were being ignored, causing browsers to use default file handling instead of the specified behavior. Changes: - Modified `setResponseHeaders()` to accept the HTTP request object - Added logic to process S3 passthrough headers from query parameters - Updated all call sites to pass the request object - Supports all AWS S3 response override parameters: - response-content-disposition - response-content-type - response-cache-control - response-content-encoding - response-content-language - response-expires The implementation follows the same pattern used in the filer handler and properly honors the AWS S3 API specification for presigned URLs. Testing: - Existing S3 API tests pass without modification - Build succeeds with no compilation errors * Update weed/s3api/s3api_object_handlers.go Co-authored-by: gemini-code-assist[bot] <176961590+gemini-code-assist[bot]@users.noreply.github.com> --------- Co-authored-by: gemini-code-assist[bot] <176961590+gemini-code-assist[bot]@users.noreply.github.com>
Diffstat (limited to 'weed/s3api')
-rw-r--r--weed/s3api/s3api_object_handlers.go26
1 files changed, 19 insertions, 7 deletions
diff --git a/weed/s3api/s3api_object_handlers.go b/weed/s3api/s3api_object_handlers.go
index c403698e5..1406bbf42 100644
--- a/weed/s3api/s3api_object_handlers.go
+++ b/weed/s3api/s3api_object_handlers.go
@@ -880,7 +880,7 @@ func (s3a *S3ApiServer) streamFromVolumeServers(w http.ResponseWriter, r *http.R
return newStreamErrorWithResponse(fmt.Errorf("invalid range for inline content: start=%d, end=%d, len=%d", start, end, len(entry.Content)))
}
// Validation passed - now set headers and write
- s3a.setResponseHeaders(w, entry, totalSize)
+ s3a.setResponseHeaders(w, r, entry, totalSize)
w.Header().Set("Content-Range", fmt.Sprintf("bytes %d-%d/%d", offset, offset+size-1, totalSize))
w.Header().Set("Content-Length", strconv.FormatInt(size, 10))
w.WriteHeader(http.StatusPartialContent)
@@ -888,7 +888,7 @@ func (s3a *S3ApiServer) streamFromVolumeServers(w http.ResponseWriter, r *http.R
return err
}
// Non-range request for inline content
- s3a.setResponseHeaders(w, entry, totalSize)
+ s3a.setResponseHeaders(w, r, entry, totalSize)
w.WriteHeader(http.StatusOK)
_, err := w.Write(entry.Content)
return err
@@ -908,7 +908,7 @@ func (s3a *S3ApiServer) streamFromVolumeServers(w http.ResponseWriter, r *http.R
return newStreamErrorWithResponse(fmt.Errorf("data integrity error: size %d reported but no content available", totalSize))
}
// Empty object - set headers and write status
- s3a.setResponseHeaders(w, entry, totalSize)
+ s3a.setResponseHeaders(w, r, entry, totalSize)
w.WriteHeader(http.StatusOK)
return nil
}
@@ -958,7 +958,7 @@ func (s3a *S3ApiServer) streamFromVolumeServers(w http.ResponseWriter, r *http.R
// All validation and preparation successful - NOW set headers and write status
tHeaderSet := time.Now()
- s3a.setResponseHeaders(w, entry, totalSize)
+ s3a.setResponseHeaders(w, r, entry, totalSize)
// Override/add range-specific headers if this is a range request
if isRangeRequest {
@@ -1164,7 +1164,7 @@ func (s3a *S3ApiServer) streamFromVolumeServersWithSSE(w http.ResponseWriter, r
// Set response headers
// IMPORTANT: Set ALL headers BEFORE calling WriteHeader (headers are ignored after WriteHeader)
tHeaderSet := time.Now()
- s3a.setResponseHeaders(w, entry, totalSize)
+ s3a.setResponseHeaders(w, r, entry, totalSize)
s3a.addSSEResponseHeadersFromEntry(w, r, entry, sseType)
// Override/add range-specific headers if this is a range request
@@ -1894,7 +1894,7 @@ func (s3a *S3ApiServer) addSSEResponseHeadersFromEntry(w http.ResponseWriter, r
}
// setResponseHeaders sets all standard HTTP response headers from entry metadata
-func (s3a *S3ApiServer) setResponseHeaders(w http.ResponseWriter, entry *filer_pb.Entry, totalSize int64) {
+func (s3a *S3ApiServer) setResponseHeaders(w http.ResponseWriter, r *http.Request, entry *filer_pb.Entry, totalSize int64) {
// Safety check: entry must be valid
if entry == nil {
glog.Errorf("setResponseHeaders: entry is nil")
@@ -1974,6 +1974,18 @@ func (s3a *S3ApiServer) setResponseHeaders(w http.ResponseWriter, entry *filer_p
w.Header().Set(s3_constants.AmzTagCount, strconv.Itoa(tagCount))
}
}
+
+ // Apply S3 passthrough headers from query parameters
+ // AWS S3 supports overriding response headers via query parameters like:
+ // ?response-cache-control=no-cache&response-content-type=application/json
+ // This allows presigned URLs to control how browsers handle the downloaded content
+ if r != nil {
+ for queryParam, headerValue := range r.URL.Query() {
+ if normalizedHeader, ok := s3_constants.PassThroughHeaders[strings.ToLower(queryParam)]; ok && len(headerValue) > 0 && headerValue[0] != "" {
+ w.Header().Set(normalizedHeader, headerValue[0])
+ }
+ }
+ }
}
// simpleMasterClient implements the minimal interface for streaming
@@ -2241,7 +2253,7 @@ func (s3a *S3ApiServer) HeadObjectHandler(w http.ResponseWriter, r *http.Request
// For HEAD requests, we already have all metadata - just set headers directly
totalSize := int64(filer.FileSize(objectEntryForSSE))
- s3a.setResponseHeaders(w, objectEntryForSSE, totalSize)
+ s3a.setResponseHeaders(w, r, objectEntryForSSE, totalSize)
// Check if PartNumber query parameter is present (for multipart objects)
// This logic matches the filer handler for consistency