summaryrefslogtreecommitdiff
path: root/common
diff options
context:
space:
mode:
Diffstat (limited to 'common')
-rw-r--r--common/copy.go46
1 files changed, 46 insertions, 0 deletions
diff --git a/common/copy.go b/common/copy.go
new file mode 100644
index 0000000..a896fb4
--- /dev/null
+++ b/common/copy.go
@@ -0,0 +1,46 @@
+package common
+
+/*
+Copied from https://ixday.github.io/post/golang-cancel-copy/
+*/
+
+import (
+ "context"
+ "io"
+)
+
+// here is some syntax sugar inspired by the Tomas Senart's video,
+// it allows me to inline the Reader interface
+type readerFunc func(p []byte) (n int, err error)
+
+func (rf readerFunc) Read(p []byte) (n int, err error) { return rf(p) }
+
+// Copy slightly modified function signature:
+// - context has been added in order to propagate cancellation
+// - (undo by Keuin) I do not return the number of bytes written, has it is not useful in my use case
+// - (added by Keuin) add a isCancelled return value indicating the copy is stopped by cancelling the context
+func Copy(ctx context.Context, out io.Writer, in io.Reader) (written int64, err error, isCancelled bool) {
+ isCancelled = false
+
+ // Copy will call the Reader and Writer interface multiple time, in order
+ // to copy by chunk (avoiding loading the whole file in memory).
+ // I insert the ability to cancel before read time as it is the earliest
+ // possible in the call process.
+ written, err = io.Copy(out, readerFunc(func(p []byte) (int, error) {
+
+ // golang non-blocking channel: https://gobyexample.com/non-blocking-channel-operations
+ select {
+
+ // if context has been canceled
+ case <-ctx.Done():
+ // stop process and propagate "context canceled" error
+ isCancelled = true
+ return 0, ctx.Err()
+ default:
+ // otherwise just run default io.Reader implementation
+ return in.Read(p)
+ }
+ }))
+
+ return
+}