diff --git a/object.go b/object.go index 3275e10..9ffb08b 100644 --- a/object.go +++ b/object.go @@ -156,12 +156,16 @@ func (s3 *S3) FilePut(u UploadInput) (PutResponse, error) { return PutResponse{}, err } - content := make([]byte, fSize) - _, err = u.Body.Read(content) - if err != nil { + if _, err := u.Body.Seek(0, io.SeekStart); err != nil { + return PutResponse{}, err + } + h := sha256.New() + if _, err := io.Copy(h, u.Body); err != nil { + return PutResponse{}, err + } + if _, err := u.Body.Seek(0, io.SeekStart); err != nil { return PutResponse{}, err } - u.Body.Seek(0, 0) req, er := http.NewRequest(http.MethodPut, s3.getURL(u.Bucket, u.ObjectKey), u.Body) if er != nil { @@ -172,8 +176,6 @@ func (s3 *S3) FilePut(u UploadInput) (PutResponse, error) { u.ContentType = "application/octet-stream" } - h := sha256.New() - h.Write(content) req.Header.Set("x-amz-content-sha256", fmt.Sprintf("%x", h.Sum(nil))) req.Header.Set("Content-Type", u.ContentType) diff --git a/object_test.go b/object_test.go index c2d72f4..c4d77e4 100644 --- a/object_test.go +++ b/object_test.go @@ -2,14 +2,55 @@ package simples3 import ( "bytes" + "crypto/sha256" "fmt" "io" + "net/http" + "net/http/httptest" "os" "strings" "testing" "time" ) +type limitedReadSeeker struct { + *bytes.Reader + maxRead int +} + +func (r *limitedReadSeeker) Read(p []byte) (int, error) { + if len(p) > r.maxRead { + p = p[:r.maxRead] + } + return r.Reader.Read(p) +} + +func TestFilePutHashesCompleteBodyWithShortReads(t *testing.T) { + content := []byte("body returned across multiple short reads") + wantHash := fmt.Sprintf("%x", sha256.Sum256(content)) + server := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { + body, err := io.ReadAll(r.Body) + if err != nil { + t.Errorf("read uploaded body: %v", err) + } + if gotHash := fmt.Sprintf("%x", sha256.Sum256(body)); gotHash != wantHash { + t.Errorf("uploaded body hash = %s, want %s", gotHash, wantHash) + } + if got := r.Header.Get("x-amz-content-sha256"); got != wantHash { + t.Errorf("x-amz-content-sha256 = %s, want %s", got, wantHash) + } + w.Header().Set("ETag", "test-etag") + w.WriteHeader(http.StatusOK) + })) + defer server.Close() + + s3 := New("us-east-1", "access", "secret").SetEndpoint(server.URL) + body := &limitedReadSeeker{Reader: bytes.NewReader(content), maxRead: 2} + if _, err := s3.FilePut(UploadInput{Bucket: "bucket", ObjectKey: "key", Body: body}); err != nil { + t.Fatalf("FilePut returned an error: %v", err) + } +} + func TestS3_FileUploadPostAndPut(t *testing.T) { testTxt, err := os.Open("testdata/test.txt") if err != nil {