From a9b24f64ee93b43ed7e3595fd8f85b198df167db Mon Sep 17 00:00:00 2001 From: Evgenii Baidakov Date: Tue, 8 Aug 2023 14:29:56 +0400 Subject: [PATCH] slicer: Fixed object upload in some cases The case when the reader returns io.EOF with the last data chunk. Slicer was closing the writer without payload writing. Signed-off-by: Evgenii Baidakov --- object/slicer/slicer.go | 4 ++-- object/slicer/slicer_test.go | 31 +++++++++++++++++++++++++++++++ 2 files changed, 33 insertions(+), 2 deletions(-) diff --git a/object/slicer/slicer.go b/object/slicer/slicer.go index 768072186..4c8f8ab5e 100644 --- a/object/slicer/slicer.go +++ b/object/slicer/slicer.go @@ -177,9 +177,9 @@ func slice(ctx context.Context, ow ObjectWriter, header object.Object, data io.R if !errors.Is(err, io.EOF) { return rootID, fmt.Errorf("read payload chunk: %w", err) } + } - // no more data to read - + if n == 0 { if err = writer.Close(); err != nil { return rootID, fmt.Errorf("writer close: %w", err) } diff --git a/object/slicer/slicer_test.go b/object/slicer/slicer_test.go index b4e520cb5..fd61291ca 100644 --- a/object/slicer/slicer_test.go +++ b/object/slicer/slicer_test.go @@ -269,6 +269,23 @@ func testSlicer(t *testing.T, size, sizeLimit uint64) { } } +// eofOnLastChunkReader is a special reader for tests. It returns io.EOF with the last data portion. +type eofOnLastChunkReader struct { + Payload []byte + point int +} + +func (l *eofOnLastChunkReader) Read(p []byte) (int, error) { + n := copy(p, l.Payload[l.point:]) + l.point += n + + if l.point == len(l.Payload) { + return n, io.EOF + } + + return n, nil +} + func testSlicerByHeaderType(t *testing.T, checker *slicedObjectChecker, in input, opts slicer.Options) { ctx := context.Background() @@ -296,6 +313,20 @@ func testSlicerByHeaderType(t *testing.T, checker *slicedObjectChecker, in input checker.chainCollector.verify(checker.input, rootID) }) + t.Run("slicer.Put, io.EOF in last chunk", func(t *testing.T) { + checker.chainCollector = newChainCollector(t) + + var hdr object.Object + hdr.SetSessionToken(opts.Session()) + hdr.SetContainerID(in.container) + hdr.SetOwnerID(&in.owner) + hdr.SetAttributes(in.attributes...) + + rootID, err := slicer.Put(ctx, checker, hdr, checker.input.signer, &eofOnLastChunkReader{Payload: in.payload}, opts) + require.NoError(t, err) + checker.chainCollector.verify(checker.input, rootID) + }) + t.Run("Slicer.InitPut", func(t *testing.T) { checker.chainCollector = newChainCollector(t)