mirror of
https://github.com/pgsty/minio.git
synced 2026-10-18 23:46:11 +03:00
fix: name the checksum algorithm in PutObject/UploadPart mismatch errors
hash.ChecksumMismatch now carries the requested algorithm (CRC32, CRC32C, SHA1, SHA256, CRC64NVME), and toAPIError renders it S3-style: The CRC32 you specified did not match the calculated checksum. instead of the generic "The provided 'x-amz-checksum' header does not match what was computed.", for all four mismatch construction sites (streaming reader, trailing-checksum decode, and both Checksum.Matches paths). Mirrors the wording already shipped for CompleteMultipartUpload in #225. Fixes #226
This commit is contained in:
@@ -2505,6 +2505,14 @@ func toAPIError(ctx context.Context, err error) APIError {
|
|||||||
case errors.Is(err, errMissingPartChecksum):
|
case errors.Is(err, errMissingPartChecksum):
|
||||||
apiErr.Description = strings.TrimPrefix(err.Error(), errMissingPartChecksum.Error()+": ")
|
apiErr.Description = strings.TrimPrefix(err.Error(), errMissingPartChecksum.Error()+": ")
|
||||||
}
|
}
|
||||||
|
// PutObject/UploadPart checksum mismatches name the algorithm in the
|
||||||
|
// message, matching S3 (e.g. "The CRC32 you specified did not match the
|
||||||
|
// calculated checksum."). Falls through to the generic table description
|
||||||
|
// when the algorithm is unknown.
|
||||||
|
var checksumMismatch hash.ChecksumMismatch
|
||||||
|
if errors.As(err, &checksumMismatch) && checksumMismatch.Algorithm != "" {
|
||||||
|
apiErr.Description = fmt.Sprintf("The %s you specified did not match the calculated checksum.", checksumMismatch.Algorithm)
|
||||||
|
}
|
||||||
switch apiErr.Code {
|
switch apiErr.Code {
|
||||||
case "NotImplemented":
|
case "NotImplemented":
|
||||||
apiErr = APIError{
|
apiErr = APIError{
|
||||||
|
|||||||
@@ -62,12 +62,13 @@ func testAPIPutObjectChunkedChecksum(obj ObjectLayer, instanceType, bucketName s
|
|||||||
// A well-formed 4-byte value that does not match the content.
|
// A well-formed 4-byte value that does not match the content.
|
||||||
wrongCRC := base64.StdEncoding.EncodeToString([]byte{0x01, 0x02, 0x03, 0x04})
|
wrongCRC := base64.StdEncoding.EncodeToString([]byte{0x01, 0x02, 0x03, 0x04})
|
||||||
|
|
||||||
apiCode := func(rec *httptest.ResponseRecorder) string {
|
apiErrOf := func(rec *httptest.ResponseRecorder) APIErrorResponse {
|
||||||
var apiErr APIErrorResponse
|
var apiErr APIErrorResponse
|
||||||
b, _ := io.ReadAll(rec.Body)
|
b, _ := io.ReadAll(rec.Body)
|
||||||
_ = xml.Unmarshal(b, &apiErr)
|
_ = xml.Unmarshal(b, &apiErr)
|
||||||
return apiErr.Code
|
return apiErr
|
||||||
}
|
}
|
||||||
|
apiCode := func(rec *httptest.ResponseRecorder) string { return apiErrOf(rec).Code }
|
||||||
|
|
||||||
// newChunkedJavaForm builds a non-trailer signed chunked PutObject request that
|
// newChunkedJavaForm builds a non-trailer signed chunked PutObject request that
|
||||||
// mirrors the AWS Java SDK v2 wire form: the checksum value sits in the header
|
// mirrors the AWS Java SDK v2 wire form: the checksum value sits in the header
|
||||||
@@ -131,8 +132,12 @@ func testAPIPutObjectChunkedChecksum(obj ObjectLayer, instanceType, bucketName s
|
|||||||
if rec.Code != http.StatusBadRequest {
|
if rec.Code != http.StatusBadRequest {
|
||||||
t.Fatalf("%s: chunked+wrong CRC32: expected 400, got %d", instanceType, rec.Code)
|
t.Fatalf("%s: chunked+wrong CRC32: expected 400, got %d", instanceType, rec.Code)
|
||||||
}
|
}
|
||||||
if code := apiCode(rec); code != "XAmzContentChecksumMismatch" {
|
gotErr := apiErrOf(rec)
|
||||||
t.Fatalf("%s: chunked+wrong CRC32: want XAmzContentChecksumMismatch, got %q", instanceType, code)
|
if gotErr.Code != "XAmzContentChecksumMismatch" {
|
||||||
|
t.Fatalf("%s: chunked+wrong CRC32: want XAmzContentChecksumMismatch, got %q", instanceType, gotErr.Code)
|
||||||
|
}
|
||||||
|
if gotErr.Message != "The CRC32 you specified did not match the calculated checksum." {
|
||||||
|
t.Fatalf("%s: chunked+wrong CRC32: message = %q, want S3 algorithm-specific wording", instanceType, gotErr.Message)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -584,6 +584,7 @@ func (c Checksum) Matches(content []byte, parts int) error {
|
|||||||
sum := hasher.Sum(nil)
|
sum := hasher.Sum(nil)
|
||||||
if c.WantParts > 0 && c.WantParts != parts {
|
if c.WantParts > 0 && c.WantParts != parts {
|
||||||
return ChecksumMismatch{
|
return ChecksumMismatch{
|
||||||
|
Algorithm: c.Type.String(),
|
||||||
Want: fmt.Sprintf("%s-%d", c.Encoded, c.WantParts),
|
Want: fmt.Sprintf("%s-%d", c.Encoded, c.WantParts),
|
||||||
Got: fmt.Sprintf("%s-%d", base64.StdEncoding.EncodeToString(sum), parts),
|
Got: fmt.Sprintf("%s-%d", base64.StdEncoding.EncodeToString(sum), parts),
|
||||||
}
|
}
|
||||||
@@ -591,6 +592,7 @@ func (c Checksum) Matches(content []byte, parts int) error {
|
|||||||
|
|
||||||
if !bytes.Equal(sum, c.Raw) {
|
if !bytes.Equal(sum, c.Raw) {
|
||||||
return ChecksumMismatch{
|
return ChecksumMismatch{
|
||||||
|
Algorithm: c.Type.String(),
|
||||||
Want: c.Encoded,
|
Want: c.Encoded,
|
||||||
Got: base64.StdEncoding.EncodeToString(sum),
|
Got: base64.StdEncoding.EncodeToString(sum),
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -74,6 +74,9 @@ func (e SizeMismatch) Error() string {
|
|||||||
|
|
||||||
// ChecksumMismatch - when content checksum does not match with what was sent from client.
|
// ChecksumMismatch - when content checksum does not match with what was sent from client.
|
||||||
type ChecksumMismatch struct {
|
type ChecksumMismatch struct {
|
||||||
|
// Algorithm carries the requested checksum type (e.g. "CRC32") so API
|
||||||
|
// error rendering can name it, matching S3's wording.
|
||||||
|
Algorithm string
|
||||||
Want string
|
Want string
|
||||||
Got string
|
Got string
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -302,11 +302,12 @@ func (r *Reader) Read(p []byte) (int, error) {
|
|||||||
r.contentHash.Encoded = r.trailer.Get(r.contentHash.Type.Key())
|
r.contentHash.Encoded = r.trailer.Get(r.contentHash.Type.Key())
|
||||||
r.contentHash.Raw, err = base64.StdEncoding.DecodeString(r.contentHash.Encoded)
|
r.contentHash.Raw, err = base64.StdEncoding.DecodeString(r.contentHash.Encoded)
|
||||||
if err != nil || len(r.contentHash.Raw) == 0 {
|
if err != nil || len(r.contentHash.Raw) == 0 {
|
||||||
return 0, ChecksumMismatch{Got: r.contentHash.Encoded}
|
return 0, ChecksumMismatch{Algorithm: r.contentHash.Type.String(), Got: r.contentHash.Encoded}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
if sum := r.contentHasher.Sum(nil); !bytes.Equal(r.contentHash.Raw, sum) {
|
if sum := r.contentHasher.Sum(nil); !bytes.Equal(r.contentHash.Raw, sum) {
|
||||||
err := ChecksumMismatch{
|
err := ChecksumMismatch{
|
||||||
|
Algorithm: r.contentHash.Type.String(),
|
||||||
Want: r.contentHash.Encoded,
|
Want: r.contentHash.Encoded,
|
||||||
Got: base64.StdEncoding.EncodeToString(sum),
|
Got: base64.StdEncoding.EncodeToString(sum),
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user