From 09ced7448d3652e914478b4ff104ee5a633d4f70 Mon Sep 17 00:00:00 2001 From: chrislu Date: Sat, 15 Nov 2025 01:10:13 -0800 Subject: [PATCH] Update s3api_object_handlers.go --- weed/s3api/s3api_object_handlers.go | 31 ++++++++++++++++++++--------- 1 file changed, 22 insertions(+), 9 deletions(-) diff --git a/weed/s3api/s3api_object_handlers.go b/weed/s3api/s3api_object_handlers.go index 7e5016f2b..3ea73ce49 100644 --- a/weed/s3api/s3api_object_handlers.go +++ b/weed/s3api/s3api_object_handlers.go @@ -435,7 +435,7 @@ func (s3a *S3ApiServer) GetObjectHandler(w http.ResponseWriter, r *http.Request) if partNumberStr == "" { partNumberStr = r.URL.Query().Get("PartNumber") } - + // If PartNumber is specified, set headers and modify Range to read only that part // This replicates the filer handler logic if partNumberStr != "" { @@ -446,15 +446,15 @@ func (s3a *S3ApiServer) GetObjectHandler(w http.ResponseWriter, r *http.Request) s3err.WriteErrorResponse(w, r, s3err.ErrInvalidPart) return } - + // Set parts count header (use actual chunk count like filer does) w.Header().Set(s3_constants.AmzMpPartsCount, strconv.Itoa(len(objectEntryForSSE.Chunks))) glog.V(3).Infof("GetObject: Set PartsCount=%d for multipart GET with PartNumber=%d", len(objectEntryForSSE.Chunks), partNumber) - + // Get the specific part chunk chunkIndex := partNumber - 1 partChunk := objectEntryForSSE.Chunks[chunkIndex] - + // Override ETag with the specific part's ETag if partChunk.ETag != "" { // chunk.ETag is base64-encoded, convert to hex for S3 compatibility @@ -464,7 +464,7 @@ func (s3a *S3ApiServer) GetObjectHandler(w http.ResponseWriter, r *http.Request) glog.V(3).Infof("GetObject: Override ETag with part %d ETag: %s", partNumber, partETag) } } - + // CRITICAL: Set Range header to read only this part's bytes (matches filer logic) // This ensures we stream only the specific part, not the entire object rangeHeader := fmt.Sprintf("bytes=%d-%d", partChunk.Offset, uint64(partChunk.Offset)+partChunk.Size-1) @@ -1073,6 +1073,19 @@ func (s3a *S3ApiServer) setResponseHeaders(w http.ResponseWriter, entry *filer_p } } } + + // Set tag count header (matches filer logic) + if entry.Extended != nil { + tagCount := 0 + for k := range entry.Extended { + if strings.HasPrefix(k, s3_constants.AmzObjectTagging+"-") { + tagCount++ + } + } + if tagCount > 0 { + w.Header().Set(s3_constants.AmzTagCount, strconv.Itoa(tagCount)) + } + } } // simpleMasterClient implements the minimal interface for streaming @@ -1226,7 +1239,7 @@ func (s3a *S3ApiServer) HeadObjectHandler(w http.ResponseWriter, r *http.Request if partNumberStr == "" { partNumberStr = r.URL.Query().Get("PartNumber") } - + // If PartNumber is specified, set headers (matching filer logic) if partNumberStr != "" { if partNumber, parseErr := strconv.Atoi(partNumberStr); parseErr == nil && partNumber > 0 { @@ -1236,15 +1249,15 @@ func (s3a *S3ApiServer) HeadObjectHandler(w http.ResponseWriter, r *http.Request s3err.WriteErrorResponse(w, r, s3err.ErrInvalidPart) return } - + // Set parts count header (use actual chunk count like filer does) w.Header().Set(s3_constants.AmzMpPartsCount, strconv.Itoa(len(objectEntryForSSE.Chunks))) glog.V(3).Infof("HeadObject: Set PartsCount=%d for part %d", len(objectEntryForSSE.Chunks), partNumber) - + // Get the specific part chunk chunkIndex := partNumber - 1 partChunk := objectEntryForSSE.Chunks[chunkIndex] - + // Override ETag with the specific part's ETag if partChunk.ETag != "" { // chunk.ETag is base64-encoded, convert to hex for S3 compatibility