Skip to content

Commit 3d771d0

Browse files
benjirewistsedgwick
authored andcommitted
GODRIVER-1925 Surface cursor errors in DownloadStream fillBuffer (mongodb#653)
1 parent c4dd952 commit 3d771d0

File tree

2 files changed

+64
-0
lines changed

2 files changed

+64
-0
lines changed

mongo/gridfs/download_stream.go

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -119,6 +119,9 @@ func (ds *DownloadStream) Close() error {
119119
}
120120

121121
ds.closed = true
122+
if ds.cursor != nil {
123+
return ds.cursor.Close(context.Background())
124+
}
122125
return nil
123126
}
124127

@@ -226,6 +229,11 @@ func (ds *DownloadStream) GetFile() *File {
226229
func (ds *DownloadStream) fillBuffer(ctx context.Context) error {
227230
if !ds.cursor.Next(ctx) {
228231
ds.done = true
232+
// Check for cursor error, otherwise there are no more chunks.
233+
if ds.cursor.Err() != nil {
234+
_ = ds.cursor.Close(ctx)
235+
return ds.cursor.Err()
236+
}
229237
return errNoMoreChunks
230238
}
231239

mongo/integration/gridfs_test.go

Lines changed: 56 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -12,6 +12,7 @@ import (
1212
"io"
1313
"math/rand"
1414
"runtime"
15+
"strings"
1516
"testing"
1617
"time"
1718

@@ -369,6 +370,61 @@ func TestGridFS(x *testing.T) {
369370
_, err = bucket.OpenDownloadStream(oid)
370371
assert.Equal(mt, gridfs.ErrMissingChunkSize, err, "expected error %v, got %v", gridfs.ErrMissingChunkSize, err)
371372
})
373+
mt.Run("cursor error during read after downloading", func(mt *mtest.T) {
374+
// To simulate a cursor error we upload a file larger than the 16MB default batch size,
375+
// so the underlying cursor remains open on the server. Since the ReadDeadline is
376+
// set in the past, Read should cause a timeout.
377+
378+
fileName := "read-error-test"
379+
fileData := make([]byte, 17000000)
380+
381+
bucket, err := gridfs.NewBucket(mt.DB)
382+
assert.Nil(mt, err, "NewBucket error: %v", err)
383+
defer func() { _ = bucket.Drop() }()
384+
385+
dataReader := bytes.NewReader(fileData)
386+
_, err = bucket.UploadFromStream(fileName, dataReader)
387+
assert.Nil(mt, err, "UploadFromStream error: %v", err)
388+
389+
ds, err := bucket.OpenDownloadStreamByName(fileName)
390+
assert.Nil(mt, err, "OpenDownloadStreamByName error: %v", err)
391+
392+
err = ds.SetReadDeadline(time.Now().Add(-1 * time.Second))
393+
assert.Nil(mt, err, "SetReadDeadline error: %v", err)
394+
395+
p := make([]byte, len(fileData))
396+
_, err = ds.Read(p)
397+
assert.NotNil(mt, err, "expected error from Read, got nil")
398+
assert.True(mt, strings.Contains(err.Error(), "context deadline exceeded"),
399+
"expected error to contain 'context deadline exceeded', got %v", err.Error())
400+
})
401+
mt.Run("cursor error during skip after downloading", func(mt *mtest.T) {
402+
// To simulate a cursor error we upload a file larger than the 16MB default batch size,
403+
// so the underlying cursor remains open on the server. Since the ReadDeadline is
404+
// set in the past, Skip should cause a timeout.
405+
406+
fileName := "skip-error-test"
407+
fileData := make([]byte, 17000000)
408+
409+
bucket, err := gridfs.NewBucket(mt.DB)
410+
assert.Nil(mt, err, "NewBucket error: %v", err)
411+
defer func() { _ = bucket.Drop() }()
412+
413+
dataReader := bytes.NewReader(fileData)
414+
_, err = bucket.UploadFromStream(fileName, dataReader)
415+
assert.Nil(mt, err, "UploadFromStream error: %v", err)
416+
417+
ds, err := bucket.OpenDownloadStreamByName(fileName)
418+
assert.Nil(mt, err, "OpenDownloadStreamByName error: %v", err)
419+
420+
err = ds.SetReadDeadline(time.Now().Add(-1 * time.Second))
421+
assert.Nil(mt, err, "SetReadDeadline error: %v", err)
422+
423+
_, err = ds.Skip(int64(len(fileData)))
424+
assert.NotNil(mt, err, "expected error from Skip, got nil")
425+
assert.True(mt, strings.Contains(err.Error(), "context deadline exceeded"),
426+
"expected error to contain 'context deadline exceeded', got %v", err.Error())
427+
})
372428
})
373429

374430
mt.RunOpts("bucket collection accessors", noClientOpts, func(mt *mtest.T) {

0 commit comments

Comments
 (0)