diff options
| author | Chris Lu <chrislusf@users.noreply.github.com> | 2025-11-26 17:17:02 -0800 |
|---|---|---|
| committer | GitHub <noreply@github.com> | 2025-11-26 17:17:02 -0800 |
| commit | ebb4f57cc73580deccfcce39cb8cd5af9682206c (patch) | |
| tree | c474acb116ac4d5c818975da5ee190e893df225d /weed | |
| parent | 4106fc0436ce91591820a59a9715a06cacf06724 (diff) | |
| download | seaweedfs-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')
| -rw-r--r-- | weed/s3api/s3api_object_handlers.go | 26 |
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 |
