bwlimit icon indicating copy to clipboard operation
bwlimit copied to clipboard

io.go: added Seek() to implement io.ReadSeeker

Open Gewinum opened this issue 1 year ago • 1 comments

I thought about adding the whole ReadSeeker class but didn't want to simply copy-paste code so I made this. Needed this for http.ServeContent

Gewinum avatar Aug 15 '24 12:08 Gewinum

The fact that this method returns an error makes me think we should not add it, let me explain.

It's not uncommon for functions to check if a io.Reader implements another interface which provides more functionality, so the function can make use of that functionality. So I don't think it's far fetched if a function would check if a reader implements io.ReadSeeker like this:

func Foo(r io.Reader) error {
	rs, ok := r.(io.ReadSeeker)
	if ok {
		// this is an io.ReadSeeker, do something differently
		i, err := rs.Seek(offset, whence)
		if err != nil {
			return err // whoops, failed to seek
		}
		...
	} else {
		// normal reader, general case
		...
	}
}

Notice that once we drop into the first branch of the if statement the code is convinced it has a valid io.ReadSeeker and an error returned from Seek would indicate that the seek failed. The same code would work if the reader wouldn't implement io.ReadSeeker, as it would drop into the second branch. So given that we can't be sure that the underlying reader actually implements io.ReadSeeker, I don't think we should add the method and advertise the reader as an io.ReadSeeker.

A workaround for this would be for you to implement your own type and make sure you construct it only if you know the underlying reader is a io.ReadSeeker:

type LimitedReadSeeker struct {
	bwlimit.Reader
}

func (r *LimitedReadSeeker) Seek(offset int64, whence int) (int64, error) {
	seekR, ok := r.Reader.(io.ReadSeeker)
	if !ok {
		return 0, errors.New("reader must be seeker")
	}
	return seekR.Seek(offset, whence)
}

lovromazgon avatar Sep 05 '24 15:09 lovromazgon