diff --git a/io.go b/io.go index b80a677..25d3a23 100644 --- a/io.go +++ b/io.go @@ -123,8 +123,8 @@ func (w *WriterAdapter) write(i []byte) { } } -// Piper doesn't block on writes. It will block on reads unless you provide a ReadTimeout -// in which case it will return, after the provided timeout, if no read is available. When closing the +// Piper doesn't block on writes. It will block on reads unless you provide a ReadTimeout in which case +// it will return an optional error, after the provided timeout, if no read is available. When closing the // piper, it will interrupt any ongoing read/future writes and return io.EOF. // Piper doesn't handle multiple readers at the same time. type Piper struct { @@ -136,7 +136,8 @@ type Piper struct { } type PiperOptions struct { - ReadTimeout time.Duration + ReadTimeout time.Duration + ReadTimeoutError error } func NewPiper(o PiperOptions) *Piper { @@ -191,7 +192,7 @@ func (p *Piper) Read(i []byte) (n int, err error) { for { // Check context if ctx != nil && ctx.Err() != nil { - return 0, nil + return 0, p.o.ReadTimeoutError } // Lock diff --git a/io_test.go b/io_test.go index 7fcb737..8f9fe08 100644 --- a/io_test.go +++ b/io_test.go @@ -157,7 +157,11 @@ func TestPiper(t *testing.T) { } // Piper should timeout on read if a read timeout is provided - p2 := NewPiper(PiperOptions{ReadTimeout: time.Millisecond}) + e1 := errors.New("1") + p2 := NewPiper(PiperOptions{ + ReadTimeout: time.Millisecond, + ReadTimeoutError: e1, + }) defer p2.Close() ctx6, cancel6 := context.WithTimeout(context.Background(), time.Second) defer cancel6() @@ -169,7 +173,7 @@ func TestPiper(t *testing.T) { if errCtx := ctx6.Err(); errors.Is(errCtx, context.DeadlineExceeded) { t.Fatalf("expected no deadline exceeded error, got %+v", errCtx) } - if err != nil { - t.Fatalf("expected nil, got %+v", err) + if !errors.Is(err, e1) { + t.Fatalf("expected %s, got %s", e1, err) } }