Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

How to serve http partial content with Go?

I have a webserver written in go and I'm serving some audio files from different sources (local, other servers, S3). I want to enable serving partial content for this files so the HTML audio tags are able to seek and loop.

How can I achive this? I know that the http package ServeContent function does this but how can I do it by serving the files myself? I need to do it without this so that I can serve files from different sources with the same handler.

like image 836
Topo Avatar asked Apr 11 '16 05:04

Topo


People also ask

What is partial HTTP request?

An HTTP range request asks the server to send only a portion of an HTTP message back to a client. Range requests are useful for clients like media players that support random access, data tools that know they need only part of a large file, and download managers that let the user pause and resume the download.

What is http partial response?

The HTTP 206 Partial Content success status response code indicates that the request has succeeded and the body contains the requested ranges of data, as described in the Range header of the request.

How does HTTP 206 work?

The HTTP Status Code 206 means that the server is delivering only part of the resource requested by the client due to a range header sent by the client. The range header is used by HTTP clients to enable resuming of interrupted downloads, or split a download into multiple simultaneous streams.

What is content range in HTTP header?

The Content-Range response HTTP header indicates where in a full body message a partial message belongs.


1 Answers

Serving partial content is non-trivial. See Byte serving on wikipedia for an introduction. You have to handle specific status codes and headers (both request and response), which is not too hard but you shouldn't waste your time doing that yourself.

If the content to serve (or serve from) is a file, you may use http.ServeFile() just as you mentioned, which handles serving partial content (Range requests).

If the content to be served is not present as a file, then http.ServeContent() is your friend:

func ServeContent(w ResponseWriter, req *Request,
    name string, modtime time.Time, content io.ReadSeeker)

And yes, it also handles serving partial content (Range requests):

The main benefit of ServeContent over io.Copy is that it handles Range requests properly, sets the MIME type, and handles If-Modified-Since requests.

All you need to do is provide an io.ReadSeeker "view" of your content, this is required so that the implementation can "jump" to the part that is requested by the client, the part that needs to be served. You might ask: how to do that?

The bytes package contains a type that implements io.ReadSeeker: bytes.Reader.

So for example if you have the content as a []byte, you may obtain an io.ReadSeeker like this:

var content []byte
// fill content
r := bytes.NewReader(content)

And what if you don't have the whole content as a []byte? One option is to provide a value of your own type that implements io.ReadSeeker.

io.ReadSeeker is:

type ReadSeeker interface {
    Reader
    Seeker
}

io.Reader contains one method:

Read(p []byte) (n int, err error)

io.Seeker also contains one method:

Seek(offset int64, whence int) (int64, error)

Your content is accessible somewhere, somehow, you know how. Seek() is called to let you know what part (position) is required from your content, and Read() is called so you can populate the passed slice (to provide the actual content). Note that these methods may be called multiple times, so you have to keep track where you are in your content (source). If you choose to go down this path, please read the doc of the linked interfaces to make sure you meet the "general contract" of the interfaces to avoid surprise errors.

like image 135
icza Avatar answered Oct 13 '22 01:10

icza