Description
net/http.ServeContent
is an extremely useful utility for serving byte blobs and files. However, it requires an io.ReadSeeker
with a valid Seek
, so that it can a) determine the total size and b) seek around in the file to serve content ranges.
I recently found myself in a situation where I had an io.Reader
, and really wanted to be able to handle Accept-Range
, If-Not-Modified
, etc. I had the total size and modtime ahead of time, so that was taken care of. But because ServeContent
takes an io.ReadSeeker
, I was left with two choices: either wrap the stream in an object that can do a fake "seek", or reimplement stuff like Accept-Range
parsing, which is super fiddly.
I think my situation probably isn't that uncommon - any time you're serving data from an underlying store or database which supports streaming data off disk, but doesn't let you seek around in it, and you want to serve that over HTTP with Accept-Range
support, you end up with this problem. To that end, I'd like to propose a sister method to ServeContent
, maybe something like:
func ServeContentStream(w ResponseWriter, req *Request, name string, modtime time.Time, size int64, content io.Reader)
The implementation should be pretty simple. serveContent
, which ServeContent
backends to, already accepts a size argument (or more accurately, a sizeFunc
). It could be changed to take an io.Reader
as well, and attempt to type-assert the reader to an io.Seeker
when and if it needs to. If that fails, it could discard bytes off the stream in order to serve successive ranges.
Note that the bits that require Seek
are only for Accept-Range
, where the ranges are known in advance and can be ordered; a backwards seek is never required. Discarding bytes is obviously not ideal; but it's way better than buffering the whole blob into memory to wrap it in a bytes.Reader
.
Obviously, I'd be happy to put in the work myself to make this change! Just filing an issue first since that's what the docs say to do. =)