Skip to content

Commit a7efd3f

Browse files
cmagliematteosuppo
authored andcommitted
Using context.Context for process cancelation
1 parent 5fdc569 commit a7efd3f

3 files changed

Lines changed: 45 additions & 78 deletions

File tree

cancelable_reader.go

Lines changed: 9 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -1,31 +1,32 @@
11
package extract
22

33
import (
4+
"context"
45
"errors"
56
"io"
67
)
78

8-
func copyCancel(dst io.Writer, src io.Reader, cancel <-chan bool) (int64, error) {
9-
return io.Copy(dst, newCancelableReader(src, cancel))
9+
func copyCancel(ctx context.Context, dst io.Writer, src io.Reader) (int64, error) {
10+
return io.Copy(dst, newCancelableReader(ctx, src))
1011
}
1112

1213
type cancelableReader struct {
13-
cancel <-chan bool
14-
src io.Reader
14+
ctx context.Context
15+
src io.Reader
1516
}
1617

1718
func (r *cancelableReader) Read(p []byte) (int, error) {
1819
select {
19-
case <-r.cancel:
20+
case <-r.ctx.Done():
2021
return 0, errors.New("interrupted")
2122
default:
2223
return r.src.Read(p)
2324
}
2425
}
2526

26-
func newCancelableReader(src io.Reader, cancel <-chan bool) *cancelableReader {
27+
func newCancelableReader(ctx context.Context, src io.Reader) *cancelableReader {
2728
return &cancelableReader{
28-
cancel: cancel,
29-
src: src,
29+
ctx: ctx,
30+
src: src,
3031
}
3132
}

extract.go

Lines changed: 26 additions & 61 deletions
Original file line numberDiff line numberDiff line change
@@ -32,6 +32,7 @@ import (
3232
"bytes"
3333
"compress/bzip2"
3434
"compress/gzip"
35+
"context"
3536
"io"
3637
"io/ioutil"
3738
"os"
@@ -52,44 +53,29 @@ type Renamer func(string) string
5253
// It automatically detects the archive type and accepts a rename function to
5354
// handle the names of the files.
5455
// If the file is not an archive, an error is returned.
55-
func Archive(body io.Reader, location string, rename Renamer) error {
56-
dummy := make(chan bool, 1)
57-
defer close(dummy)
58-
return ArchiveCancel(body, location, rename, dummy)
59-
}
60-
61-
// ArchiveCancel is the same as Archive but with an extra channel to stop
62-
// the extraction.
63-
func ArchiveCancel(body io.Reader, location string, rename Renamer, cancel <-chan bool) error {
56+
func Archive(ctx context.Context, body io.Reader, location string, rename Renamer) error {
6457
body, kind, err := match(body)
6558
if err != nil {
6659
errors.Annotatef(err, "Detect archive type")
6760
}
6861

6962
switch kind.Extension {
7063
case "zip":
71-
return ZipCancel(body, location, rename, cancel)
64+
return Zip(ctx, body, location, rename)
7265
case "gz":
73-
return GzCancel(body, location, rename, cancel)
66+
return Gz(ctx, body, location, rename)
7467
case "bz2":
75-
return Bz2Cancel(body, location, rename, cancel)
68+
return Bz2(ctx, body, location, rename)
7669
case "tar":
77-
return TarCancel(body, location, rename, cancel)
70+
return Tar(ctx, body, location, rename)
7871
default:
7972
return errors.New("Not a supported archive")
8073
}
8174
}
8275

8376
// Bz2 extracts a .bz2 or .tar.bz2 archived stream of data in the specified location.
8477
// It accepts a rename function to handle the names of the files (see the example)
85-
func Bz2(body io.Reader, location string, rename Renamer) error {
86-
dummy := make(chan bool, 1)
87-
defer close(dummy)
88-
return Bz2Cancel(body, location, rename, dummy)
89-
}
90-
91-
// Bz2Cancel is the same as Bz2 but with an extra channel to stop the extraction.
92-
func Bz2Cancel(body io.Reader, location string, rename Renamer, cancel <-chan bool) error {
78+
func Bz2(ctx context.Context, body io.Reader, location string, rename Renamer) error {
9379
reader := bzip2.NewReader(body)
9480

9581
body, kind, err := match(reader)
@@ -98,10 +84,10 @@ func Bz2Cancel(body io.Reader, location string, rename Renamer, cancel <-chan bo
9884
}
9985

10086
if kind.Extension == "tar" {
101-
return TarCancel(body, location, rename, cancel)
87+
return Tar(ctx, body, location, rename)
10288
}
10389

104-
err = copy(location, 0666, body, cancel)
90+
err = copy(ctx, location, 0666, body)
10591
if err != nil {
10692
return err
10793
}
@@ -110,14 +96,7 @@ func Bz2Cancel(body io.Reader, location string, rename Renamer, cancel <-chan bo
11096

11197
// Gz extracts a .gz or .tar.gz archived stream of data in the specified location.
11298
// It accepts a rename function to handle the names of the files (see the example)
113-
func Gz(body io.Reader, location string, rename Renamer) error {
114-
dummy := make(chan bool, 1)
115-
defer close(dummy)
116-
return GzCancel(body, location, rename, dummy)
117-
}
118-
119-
// GzCancel is the same as Gz but with an extra channel to stop the extraction.
120-
func GzCancel(body io.Reader, location string, rename Renamer, cancel <-chan bool) error {
99+
func Gz(ctx context.Context, body io.Reader, location string, rename Renamer) error {
121100
reader, err := gzip.NewReader(body)
122101
if err != nil {
123102
return errors.Annotatef(err, "Gunzip")
@@ -129,9 +108,9 @@ func GzCancel(body io.Reader, location string, rename Renamer, cancel <-chan boo
129108
}
130109

131110
if kind.Extension == "tar" {
132-
return TarCancel(body, location, rename, cancel)
111+
return Tar(ctx, body, location, rename)
133112
}
134-
err = copy(location, 0666, body, cancel)
113+
err = copy(ctx, location, 0666, body)
135114
if err != nil {
136115
return err
137116
}
@@ -150,14 +129,7 @@ type link struct {
150129

151130
// Tar extracts a .tar archived stream of data in the specified location.
152131
// It accepts a rename function to handle the names of the files (see the example)
153-
func Tar(body io.Reader, location string, rename Renamer) error {
154-
dummy := make(chan bool, 1)
155-
defer close(dummy)
156-
return TarCancel(body, location, rename, dummy)
157-
}
158-
159-
// TarCancel is the same as Tar but with an extra channel to stop the extraction.
160-
func TarCancel(body io.Reader, location string, rename Renamer, cancel <-chan bool) error {
132+
func Tar(ctx context.Context, body io.Reader, location string, rename Renamer) error {
161133
files := []file{}
162134
links := []link{}
163135
symlinks := []link{}
@@ -167,7 +139,7 @@ func TarCancel(body io.Reader, location string, rename Renamer, cancel <-chan bo
167139
tr := tar.NewReader(body)
168140
for {
169141
select {
170-
case <-cancel:
142+
case <-ctx.Done():
171143
return errors.New("interrupted")
172144
default:
173145
}
@@ -200,7 +172,7 @@ func TarCancel(body io.Reader, location string, rename Renamer, cancel <-chan bo
200172
}
201173
case tar.TypeReg, tar.TypeRegA:
202174
var data bytes.Buffer
203-
if _, err := copyCancel(&data, tr, cancel); err != nil {
175+
if _, err := copyCancel(ctx, &data, tr); err != nil {
204176
return errors.Annotatef(err, "Read contents of file %s", path)
205177
}
206178
files = append(files, file{Path: path, Mode: info.Mode(), Data: data})
@@ -219,14 +191,14 @@ func TarCancel(body io.Reader, location string, rename Renamer, cancel <-chan bo
219191

220192
// Now we make another pass creating the files and links
221193
for i := range files {
222-
if err := copy(files[i].Path, files[i].Mode, &files[i].Data, cancel); err != nil {
194+
if err := copy(ctx, files[i].Path, files[i].Mode, &files[i].Data); err != nil {
223195
return errors.Annotatef(err, "Create file %s", files[i].Path)
224196
}
225197
}
226198

227199
for i := range links {
228200
select {
229-
case <-cancel:
201+
case <-ctx.Done():
230202
return errors.New("interrupted")
231203
default:
232204
}
@@ -237,7 +209,7 @@ func TarCancel(body io.Reader, location string, rename Renamer, cancel <-chan bo
237209

238210
for i := range symlinks {
239211
select {
240-
case <-cancel:
212+
case <-ctx.Done():
241213
return errors.New("interrupted")
242214
default:
243215
}
@@ -250,17 +222,10 @@ func TarCancel(body io.Reader, location string, rename Renamer, cancel <-chan bo
250222

251223
// Zip extracts a .zip archived stream of data in the specified location.
252224
// It accepts a rename function to handle the names of the files (see the example).
253-
func Zip(body io.Reader, location string, rename Renamer) error {
254-
dummy := make(chan bool, 1)
255-
defer close(dummy)
256-
return ZipCancel(body, location, rename, dummy)
257-
}
258-
259-
// ZipCancel is the same as Bz2 but with an extra channel to stop the extraction.
260-
func ZipCancel(body io.Reader, location string, rename Renamer, cancel <-chan bool) error {
225+
func Zip(ctx context.Context, body io.Reader, location string, rename Renamer) error {
261226
// read the whole body into a buffer. Not sure this is the best way to do it
262227
buffer := bytes.NewBuffer([]byte{})
263-
copyCancel(buffer, body, cancel)
228+
copyCancel(ctx, buffer, body)
264229

265230
archive, err := zip.NewReader(bytes.NewReader(buffer.Bytes()), int64(buffer.Len()))
266231
if err != nil {
@@ -274,7 +239,7 @@ func ZipCancel(body io.Reader, location string, rename Renamer, cancel <-chan bo
274239
// attempting to create a file where there's no folder
275240
for _, header := range archive.File {
276241
select {
277-
case <-cancel:
242+
case <-ctx.Done():
278243
return errors.New("interrupted")
279244
default:
280245
}
@@ -313,7 +278,7 @@ func ZipCancel(body io.Reader, location string, rename Renamer, cancel <-chan bo
313278
return errors.Annotatef(err, "Open file %s", path)
314279
}
315280
var data bytes.Buffer
316-
if _, err := copyCancel(&data, f, cancel); err != nil {
281+
if _, err := copyCancel(ctx, &data, f); err != nil {
317282
return errors.Annotatef(err, "Read contents of file %s", path)
318283
}
319284
files = append(files, file{Path: path, Mode: info.Mode(), Data: data})
@@ -322,14 +287,14 @@ func ZipCancel(body io.Reader, location string, rename Renamer, cancel <-chan bo
322287

323288
// Now we make another pass creating the files and links
324289
for i := range files {
325-
if err := copy(files[i].Path, files[i].Mode, &files[i].Data, cancel); err != nil {
290+
if err := copy(ctx, files[i].Path, files[i].Mode, &files[i].Data); err != nil {
326291
return errors.Annotatef(err, "Create file %s", files[i].Path)
327292
}
328293
}
329294

330295
for i := range links {
331296
select {
332-
case <-cancel:
297+
case <-ctx.Done():
333298
return errors.New("interrupted")
334299
default:
335300
}
@@ -341,7 +306,7 @@ func ZipCancel(body io.Reader, location string, rename Renamer, cancel <-chan bo
341306
return nil
342307
}
343308

344-
func copy(path string, mode os.FileMode, src io.Reader, cancel <-chan bool) error {
309+
func copy(ctx context.Context, path string, mode os.FileMode, src io.Reader) error {
345310
// We add the execution permission to be able to create files inside it
346311
err := os.MkdirAll(filepath.Dir(path), mode|os.ModeDir|100)
347312
if err != nil {
@@ -352,7 +317,7 @@ func copy(path string, mode os.FileMode, src io.Reader, cancel <-chan bool) erro
352317
return err
353318
}
354319
defer file.Close()
355-
_, err = copyCancel(file, src, cancel)
320+
_, err = copyCancel(ctx, file, src)
356321
return err
357322
}
358323

extract_test.go

Lines changed: 10 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,7 @@ package extract_test
22

33
import (
44
"bytes"
5+
"context"
56
"io/ioutil"
67
"os"
78
"path/filepath"
@@ -149,7 +150,7 @@ var ExtractCases = []struct {
149150
}
150151

151152
func TestArchiveFailure(t *testing.T) {
152-
err := extract.Archive(strings.NewReader("not an archive"), "", nil)
153+
err := extract.Archive(context.Background(), strings.NewReader("not an archive"), "", nil)
153154
if err == nil || err.Error() != "Not a supported archive" {
154155
t.Error("Expected error 'Not a supported archive', got", err)
155156
}
@@ -167,13 +168,13 @@ func TestExtract(t *testing.T) {
167168

168169
switch filepath.Ext(test.Archive) {
169170
case ".bz2":
170-
err = extract.Bz2(buffer, dir, test.Renamer)
171+
err = extract.Bz2(context.Background(), buffer, dir, test.Renamer)
171172
case ".gz":
172-
err = extract.Gz(buffer, dir, test.Renamer)
173+
err = extract.Gz(context.Background(), buffer, dir, test.Renamer)
173174
case ".zip":
174-
err = extract.Zip(buffer, dir, test.Renamer)
175+
err = extract.Zip(context.Background(), buffer, dir, test.Renamer)
175176
case ".mistery":
176-
err = extract.Archive(buffer, dir, test.Renamer)
177+
err = extract.Archive(context.Background(), buffer, dir, test.Renamer)
177178
default:
178179
t.Fatal("unknown error")
179180
}
@@ -243,7 +244,7 @@ func BenchmarkArchive(b *testing.B) {
243244

244245
for i := 0; i < b.N; i++ {
245246
buffer := bytes.NewBuffer(data)
246-
err := extract.Archive(buffer, filepath.Join(dir, strconv.Itoa(i)), nil)
247+
err := extract.Archive(context.Background(), buffer, filepath.Join(dir, strconv.Itoa(i)), nil)
247248
if err != nil {
248249
b.Error(err)
249250
}
@@ -265,7 +266,7 @@ func BenchmarkTarBz2(b *testing.B) {
265266

266267
for i := 0; i < b.N; i++ {
267268
buffer := bytes.NewBuffer(data)
268-
err := extract.Bz2(buffer, filepath.Join(dir, strconv.Itoa(i)), nil)
269+
err := extract.Bz2(context.Background(), buffer, filepath.Join(dir, strconv.Itoa(i)), nil)
269270
if err != nil {
270271
b.Error(err)
271272
}
@@ -287,7 +288,7 @@ func BenchmarkTarGz(b *testing.B) {
287288

288289
for i := 0; i < b.N; i++ {
289290
buffer := bytes.NewBuffer(data)
290-
err := extract.Gz(buffer, filepath.Join(dir, strconv.Itoa(i)), nil)
291+
err := extract.Gz(context.Background(), buffer, filepath.Join(dir, strconv.Itoa(i)), nil)
291292
if err != nil {
292293
b.Error(err)
293294
}
@@ -309,7 +310,7 @@ func BenchmarkZip(b *testing.B) {
309310

310311
for i := 0; i < b.N; i++ {
311312
buffer := bytes.NewBuffer(data)
312-
err := extract.Zip(buffer, filepath.Join(dir, strconv.Itoa(i)), nil)
313+
err := extract.Zip(context.Background(), buffer, filepath.Join(dir, strconv.Itoa(i)), nil)
313314
if err != nil {
314315
b.Error(err)
315316
}

0 commit comments

Comments
 (0)