From 1f5e7ce598e1d22ecd9d6c69b50106e80d8aa620 Mon Sep 17 00:00:00 2001 From: Nick Craig-Wood Date: Fri, 29 Jul 2022 16:39:02 +0100 Subject: [PATCH] lib/readers: add GzipReader --- lib/readers/gzip.go | 42 +++++++++++++++++++++++++++++++++++ lib/readers/gzip_test.go | 47 ++++++++++++++++++++++++++++++++++++++++ 2 files changed, 89 insertions(+) create mode 100644 lib/readers/gzip.go create mode 100644 lib/readers/gzip_test.go diff --git a/lib/readers/gzip.go b/lib/readers/gzip.go new file mode 100644 index 000000000..e018f2880 --- /dev/null +++ b/lib/readers/gzip.go @@ -0,0 +1,42 @@ +package readers + +import ( + "compress/gzip" + "io" +) + +// gzipReader wraps a *gzip.Reader so it closes the underlying stream +// which the gzip library doesn't. +type gzipReader struct { + *gzip.Reader + in io.ReadCloser +} + +// NewGzipReader returns an io.ReadCloser which will read the stream +// and close it when Close is called. +// +// Unfortunately gz.Reader does not close the underlying stream so we +// can't use that directly. +func NewGzipReader(in io.ReadCloser) (io.ReadCloser, error) { + zr, err := gzip.NewReader(in) + if err != nil { + return nil, err + } + return &gzipReader{ + Reader: zr, + in: in, + }, nil +} + +// Close the underlying stream and the gzip reader +func (gz *gzipReader) Close() error { + zrErr := gz.Reader.Close() + inErr := gz.in.Close() + if inErr != nil { + return inErr + } + if zrErr != nil { + return zrErr + } + return nil +} diff --git a/lib/readers/gzip_test.go b/lib/readers/gzip_test.go new file mode 100644 index 000000000..799e52f6e --- /dev/null +++ b/lib/readers/gzip_test.go @@ -0,0 +1,47 @@ +package readers + +import ( + "bytes" + "compress/gzip" + "io" + "testing" + + "github.com/rclone/rclone/lib/random" + "github.com/stretchr/testify/assert" + "github.com/stretchr/testify/require" +) + +type checkClose struct { + io.Reader + closed bool +} + +func (cc *checkClose) Close() error { + cc.closed = true + return nil +} + +func TestGzipReader(t *testing.T) { + // Create some compressed data + data := random.String(1000) + var out bytes.Buffer + zw := gzip.NewWriter(&out) + _, err := io.Copy(zw, bytes.NewBufferString(data)) + require.NoError(t, err) + require.NoError(t, zw.Close()) + gzData := out.Bytes() + + // Check we can decompress it + cc := &checkClose{Reader: bytes.NewBuffer(gzData)} + var decompressed bytes.Buffer + zr, err := NewGzipReader(cc) + require.NoError(t, err) + _, err = io.Copy(&decompressed, zr) + require.NoError(t, err) + assert.Equal(t, data, string(decompressed.Bytes())) + + // Check the underlying close gets called + assert.False(t, cc.closed) + require.NoError(t, zr.Close()) + assert.True(t, cc.closed) +}