From 55c409decf1aef16d5d61dcec883b2a9217f8d66 Mon Sep 17 00:00:00 2001 From: Chris Lu Date: Wed, 4 Mar 2026 11:32:48 -0800 Subject: [PATCH] s3api: evaluate conditional headers after version resolution Move conditional header evaluation (If-Match, If-None-Match, etc.) to after the version resolution step in GetObjectAttributesHandler. This ensures that when a specific versionId is requested, conditions are checked against the correct version entry rather than always against the latest version. Co-Authored-By: Claude Opus 4.6 --- .../s3api/s3api_object_handlers_attributes.go | 44 +++++++++++-------- 1 file changed, 26 insertions(+), 18 deletions(-) diff --git a/weed/s3api/s3api_object_handlers_attributes.go b/weed/s3api/s3api_object_handlers_attributes.go index 1597c4428..c516d528a 100644 --- a/weed/s3api/s3api_object_handlers_attributes.go +++ b/weed/s3api/s3api_object_handlers_attributes.go @@ -105,12 +105,6 @@ func (s3a *S3ApiServer) GetObjectAttributesHandler(w http.ResponseWriter, r *htt } } - // Process conditional headers - result, handled := s3a.processConditionalHeaders(w, r, bucket, object, "GetObjectAttributesHandler") - if handled { - return - } - // Check for specific version ID versionId := r.URL.Query().Get("versionId") @@ -192,19 +186,33 @@ func (s3a *S3ApiServer) GetObjectAttributesHandler(w http.ResponseWriter, r *htt w.Header().Set("x-amz-version-id", targetVersionId) } else { - // Non-versioned: reuse entry from conditional check or fetch - if result.Entry != nil { - entry = result.Entry - } else { - entry, err = s3a.fetchObjectEntry(bucket, object) - if err != nil { - s3err.WriteErrorResponse(w, r, s3err.ErrInternalError) - return - } - if entry == nil { - s3err.WriteErrorResponse(w, r, s3err.ErrNoSuchKey) - return + entry, err = s3a.fetchObjectEntry(bucket, object) + if err != nil { + s3err.WriteErrorResponse(w, r, s3err.ErrInternalError) + return + } + if entry == nil { + s3err.WriteErrorResponse(w, r, s3err.ErrNoSuchKey) + return + } + } + + // Evaluate conditional headers against the resolved entry (after version resolution) + // This ensures conditions are checked against the correct version, not always the latest + if s3a.hasConditionalHeaders(r) { + headers, errCode := parseConditionalHeaders(r) + if errCode != s3err.ErrNone { + s3err.WriteErrorResponse(w, r, errCode) + return + } + result := s3a.validateConditionalHeadersForReads(r, headers, entry, bucket, object) + if result.ErrorCode != s3err.ErrNone { + glog.V(3).Infof("GetObjectAttributesHandler: Conditional header check failed for %s/%s with error %v", bucket, object, result.ErrorCode) + if result.ErrorCode == s3err.ErrNotModified && result.ETag != "" { + w.Header().Set("ETag", result.ETag) } + s3err.WriteErrorResponse(w, r, result.ErrorCode) + return } }