feat: add ChecksumType to CompleteMultipartUpload response struct

Signed-off-by: Shooks <justanormalme@gmail.com>
This commit is contained in:
Shooks
2026-08-09 17:39:57 +08:00
parent 100e2e57a7
commit d014a12cff
3 changed files with 60 additions and 0 deletions
@@ -1811,6 +1811,7 @@
"cmd:cmd:field:CompleteMultipartUploadResponse.ChecksumCRC64NVME", "cmd:cmd:field:CompleteMultipartUploadResponse.ChecksumCRC64NVME",
"cmd:cmd:field:CompleteMultipartUploadResponse.ChecksumSHA1", "cmd:cmd:field:CompleteMultipartUploadResponse.ChecksumSHA1",
"cmd:cmd:field:CompleteMultipartUploadResponse.ChecksumSHA256", "cmd:cmd:field:CompleteMultipartUploadResponse.ChecksumSHA256",
"cmd:cmd:field:CompleteMultipartUploadResponse.ChecksumType",
"cmd:cmd:field:CompleteMultipartUploadResponse.ETag", "cmd:cmd:field:CompleteMultipartUploadResponse.ETag",
"cmd:cmd:field:CompleteMultipartUploadResponse.Key", "cmd:cmd:field:CompleteMultipartUploadResponse.Key",
"cmd:cmd:field:CompleteMultipartUploadResponse.Location", "cmd:cmd:field:CompleteMultipartUploadResponse.Location",
+2
View File
@@ -416,6 +416,7 @@ type CompleteMultipartUploadResponse struct {
Key string Key string
ETag string ETag string
ChecksumType string `xml:"ChecksumType,omitempty"`
ChecksumCRC32 string `xml:"ChecksumCRC32,omitempty"` ChecksumCRC32 string `xml:"ChecksumCRC32,omitempty"`
ChecksumCRC32C string `xml:"ChecksumCRC32C,omitempty"` ChecksumCRC32C string `xml:"ChecksumCRC32C,omitempty"`
ChecksumSHA1 string `xml:"ChecksumSHA1,omitempty"` ChecksumSHA1 string `xml:"ChecksumSHA1,omitempty"`
@@ -797,6 +798,7 @@ func generateCompleteMultipartUploadResponse(bucket, key, location string, oi Ob
Key: key, Key: key,
// AWS S3 quotes the ETag in XML, make sure we are compatible here. // AWS S3 quotes the ETag in XML, make sure we are compatible here.
ETag: "\"" + oi.ETag + "\"", ETag: "\"" + oi.ETag + "\"",
ChecksumType: cs[xhttp.AmzChecksumType],
ChecksumSHA1: cs[hash.ChecksumSHA1.String()], ChecksumSHA1: cs[hash.ChecksumSHA1.String()],
ChecksumSHA256: cs[hash.ChecksumSHA256.String()], ChecksumSHA256: cs[hash.ChecksumSHA256.String()],
ChecksumCRC32: cs[hash.ChecksumCRC32.String()], ChecksumCRC32: cs[hash.ChecksumCRC32.String()],
+57
View File
@@ -43,6 +43,7 @@ import (
"github.com/dustin/go-humanize" "github.com/dustin/go-humanize"
"github.com/minio/minio/internal/auth" "github.com/minio/minio/internal/auth"
"github.com/minio/minio/internal/crypto" "github.com/minio/minio/internal/crypto"
minhash "github.com/minio/minio/internal/hash"
"github.com/minio/minio/internal/hash/sha256" "github.com/minio/minio/internal/hash/sha256"
xhttp "github.com/minio/minio/internal/http" xhttp "github.com/minio/minio/internal/http"
ioutilx "github.com/minio/minio/internal/ioutil" ioutilx "github.com/minio/minio/internal/ioutil"
@@ -3386,6 +3387,62 @@ func testAPICompleteMultipartHandler(obj ObjectLayer, instanceType, bucketName s
ExecObjectLayerAPINilTest(t, nilBucket, nilObject, instanceType, apiRouter, nilReq) ExecObjectLayerAPINilTest(t, nilBucket, nilObject, instanceType, apiRouter, nilReq)
} }
// TestGenerateCompleteMultipartUploadResponseChecksumType verifies that
// ChecksumType is populated as FULL_OBJECT/COMPOSITE when the object carries
// a checksum, and omitted from the XML entirely when it doesn't.
func TestGenerateCompleteMultipartUploadResponseChecksumType(t *testing.T) {
bucket, key := "test-bucket", "test-object"
testCases := []struct {
name string
checksum *minhash.Checksum
wantChecksumType string
}{
{
name: "no checksum",
checksum: nil,
wantChecksumType: "",
},
{
name: "full object checksum",
checksum: minhash.NewChecksumFromData(minhash.ChecksumCRC32, []byte("full-object-data")),
wantChecksumType: xhttp.AmzChecksumTypeFullObject,
},
{
name: "composite multipart checksum",
checksum: func() *minhash.Checksum {
c := minhash.NewChecksumFromData(minhash.ChecksumCRC32C|minhash.ChecksumMultipart, []byte("combined-part-checksums"))
c.WantParts = 2
return c
}(),
wantChecksumType: xhttp.AmzChecksumTypeComposite,
},
}
for _, tt := range testCases {
t.Run(tt.name, func(t *testing.T) {
oi := ObjectInfo{ETag: "d41d8cd98f00b204e9800998ecf8427e"}
if tt.checksum != nil {
oi.Checksum = tt.checksum.AppendTo(nil, nil)
}
resp := generateCompleteMultipartUploadResponse(bucket, key, getGetObjectURL("", bucket, key), oi, nil)
if resp.ChecksumType != tt.wantChecksumType {
t.Fatalf("ChecksumType: got %q, want %q", resp.ChecksumType, tt.wantChecksumType)
}
encoded, err := xml.Marshal(resp)
if err != nil {
t.Fatalf("failed to marshal response: %v", err)
}
gotTag := strings.Contains(string(encoded), "<ChecksumType>")
if wantTag := tt.wantChecksumType != ""; gotTag != wantTag {
t.Fatalf("ChecksumType tag presence: got %v, want %v (xml: %s)", gotTag, wantTag, encoded)
}
})
}
}
// The UploadID from the response body is parsed and its existence is asserted with an attempt to ListParts using it. // The UploadID from the response body is parsed and its existence is asserted with an attempt to ListParts using it.
func TestAPIAbortMultipartHandler(t *testing.T) { func TestAPIAbortMultipartHandler(t *testing.T) {
defer DetectTestLeak(t)() defer DetectTestLeak(t)()