Merge pull request #57 from Dansyuqri/feat-add-checksumtype-completemultipartupload-response

feat: add ChecksumType to CompleteMultipartUpload response struct
This commit is contained in:
Feng Ruohang
2026-08-26 10:27:54 +08:00
committed by GitHub
3 changed files with 60 additions and 0 deletions
@@ -1811,6 +1811,7 @@
"cmd:cmd:field:CompleteMultipartUploadResponse.ChecksumCRC64NVME",
"cmd:cmd:field:CompleteMultipartUploadResponse.ChecksumSHA1",
"cmd:cmd:field:CompleteMultipartUploadResponse.ChecksumSHA256",
"cmd:cmd:field:CompleteMultipartUploadResponse.ChecksumType",
"cmd:cmd:field:CompleteMultipartUploadResponse.ETag",
"cmd:cmd:field:CompleteMultipartUploadResponse.Key",
"cmd:cmd:field:CompleteMultipartUploadResponse.Location",
+2
View File
@@ -428,6 +428,7 @@ type CompleteMultipartUploadResponse struct {
Key string
ETag string
ChecksumType string `xml:"ChecksumType,omitempty"`
ChecksumCRC32 string `xml:"ChecksumCRC32,omitempty"`
ChecksumCRC32C string `xml:"ChecksumCRC32C,omitempty"`
ChecksumSHA1 string `xml:"ChecksumSHA1,omitempty"`
@@ -821,6 +822,7 @@ func generateCompleteMultipartUploadResponse(bucket, key, location string, oi Ob
Key: key,
// AWS S3 quotes the ETag in XML, make sure we are compatible here.
ETag: "\"" + oi.ETag + "\"",
ChecksumType: cs[xhttp.AmzChecksumType],
ChecksumSHA1: cs[hash.ChecksumSHA1.String()],
ChecksumSHA256: cs[hash.ChecksumSHA256.String()],
ChecksumCRC32: cs[hash.ChecksumCRC32.String()],
+57
View File
@@ -43,6 +43,7 @@ import (
"github.com/dustin/go-humanize"
"github.com/minio/minio/internal/auth"
"github.com/minio/minio/internal/crypto"
minhash "github.com/minio/minio/internal/hash"
"github.com/minio/minio/internal/hash/sha256"
xhttp "github.com/minio/minio/internal/http"
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)
}
// 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.
func TestAPIAbortMultipartHandler(t *testing.T) {
defer DetectTestLeak(t)()