diff options
author | Keuin <[email protected]> | 2022-09-07 10:46:33 +0800 |
---|---|---|
committer | Keuin <[email protected]> | 2022-09-07 10:46:33 +0800 |
commit | c78edaa0ffa28bb360663f172e98540b7978e9b2 (patch) | |
tree | 6e99ca4c42d31eb8516fe8b16a445f3618530948 /common | |
parent | fd48122ae9c340b867bb31dbbde6a3b7a4667944 (diff) |
Handle error correctly when copying live stream.
Diffstat (limited to 'common')
-rw-r--r-- | common/copy.go | 46 |
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 +} |