s3: return IncompleteBody for a truncated PUT body
A client abort or reverse-proxy timeout truncates the request body mid-upload. putToFiler mapped every streaming-upload failure to InternalError (500), which a reverse proxy relays as a 502. Classify a source-read truncation as IncompleteBody (400) so the response matches AWS and passes through. All S3 write paths share putToFiler, so they all benefit.
This commit is contained in:
parent
029c91c860
commit
363d865049
@ -545,10 +545,7 @@ func (s3a *S3ApiServer) putToFiler(r *http.Request, filePath string, dataReader
|
||||
s3a.deleteOrphanedChunks(chunkResult.FileChunks)
|
||||
}
|
||||
|
||||
if strings.Contains(err.Error(), s3err.ErrMsgPayloadChecksumMismatch) {
|
||||
return "", s3err.ErrInvalidDigest, SSEResponseMetadata{}
|
||||
}
|
||||
return "", s3err.ErrInternalError, SSEResponseMetadata{}
|
||||
return "", mapChunkedUploadErrorToS3Error(err), SSEResponseMetadata{}
|
||||
}
|
||||
|
||||
// Step 3: Calculate MD5 hash and add SSE metadata to chunks
|
||||
@ -1170,6 +1167,22 @@ func filerErrorToS3Error(err error) s3err.ErrorCode {
|
||||
}
|
||||
}
|
||||
|
||||
// mapChunkedUploadErrorToS3Error classifies a failed streaming upload. A truncated
|
||||
// request body (client abort or reverse-proxy timeout) is a request error, so report
|
||||
// IncompleteBody (400) rather than a 500 a reverse proxy would relay as a confusing
|
||||
// 502. Only the source read is tagged, so a volume-server upload fault still maps to
|
||||
// InternalError.
|
||||
func mapChunkedUploadErrorToS3Error(err error) s3err.ErrorCode {
|
||||
switch {
|
||||
case strings.Contains(err.Error(), s3err.ErrMsgPayloadChecksumMismatch):
|
||||
return s3err.ErrInvalidDigest
|
||||
case errors.Is(err, operation.ErrTruncatedBody):
|
||||
return s3err.ErrIncompleteBody
|
||||
default:
|
||||
return s3err.ErrInternalError
|
||||
}
|
||||
}
|
||||
|
||||
// setObjectOwnerFromRequest sets the object owner metadata based on the bucket ownership policy.
|
||||
// When BucketOwnerEnforced (the modern AWS default), the bucket owner owns all objects.
|
||||
// Otherwise, the uploader's account ID is used (ObjectWriter mode).
|
||||
|
||||
51
weed/s3api/s3api_object_handlers_put_error_test.go
Normal file
51
weed/s3api/s3api_object_handlers_put_error_test.go
Normal file
@ -0,0 +1,51 @@
|
||||
package s3api
|
||||
|
||||
import (
|
||||
"errors"
|
||||
"fmt"
|
||||
"io"
|
||||
"testing"
|
||||
|
||||
"github.com/seaweedfs/seaweedfs/weed/operation"
|
||||
"github.com/seaweedfs/seaweedfs/weed/s3api/s3err"
|
||||
)
|
||||
|
||||
func TestMapChunkedUploadErrorToS3Error(t *testing.T) {
|
||||
tests := []struct {
|
||||
name string
|
||||
err error
|
||||
want s3err.ErrorCode
|
||||
}{
|
||||
{
|
||||
// A truncated body (client abort or reverse-proxy timeout) reaches
|
||||
// putToFiler tagged exactly like UploadReaderInChunks reports it.
|
||||
name: "truncated source read maps to IncompleteBody",
|
||||
err: fmt.Errorf("%w: read chunk at offset %d (got %d bytes): %w", operation.ErrTruncatedBody, 0, 8056500, io.ErrUnexpectedEOF),
|
||||
want: s3err.ErrIncompleteBody,
|
||||
},
|
||||
{
|
||||
// A volume-server upload dropping mid-write is a server fault, not a
|
||||
// client truncation, even though it also carries io.ErrUnexpectedEOF.
|
||||
name: "volume upload unexpected EOF maps to InternalError",
|
||||
err: fmt.Errorf("upload chunk: %w", io.ErrUnexpectedEOF),
|
||||
want: s3err.ErrInternalError,
|
||||
},
|
||||
{
|
||||
name: "payload checksum mismatch maps to InvalidDigest",
|
||||
err: errors.New(s3err.ErrMsgPayloadChecksumMismatch),
|
||||
want: s3err.ErrInvalidDigest,
|
||||
},
|
||||
{
|
||||
name: "other errors map to InternalError",
|
||||
err: errors.New("assign volume: no free volumes"),
|
||||
want: s3err.ErrInternalError,
|
||||
},
|
||||
}
|
||||
for _, tt := range tests {
|
||||
t.Run(tt.name, func(t *testing.T) {
|
||||
if got := mapChunkedUploadErrorToS3Error(tt.err); got != tt.want {
|
||||
t.Errorf("mapChunkedUploadErrorToS3Error(%v) = %v, want %v", tt.err, got, tt.want)
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
Loading…
Reference in New Issue
Block a user