blob: 7865b83c289b31053b4027bcc2c9316c5834804f [file] [log] [blame]
// +build ignore,OMIT
package main
import (
"io"
"log"
"net/http"
"sort"
"strings"
"time"
)
var modTime = time.Unix(1374708739, 0)
// START OMIT
func part(s string) SizeReaderAt {
return io.NewSectionReader(strings.NewReader(s), 0, int64(len(s)))
}
func handler(w http.ResponseWriter, r *http.Request) {
sra := NewMultiReaderAt(
part("Hello, "), part(" world! "),
part("You requested "+r.URL.Path+"\n"),
)
rs := io.NewSectionReader(sra, 0, sra.Size())
http.ServeContent(w, r, "foo.txt", modTime, rs)
}
//END OMIT
func main() {
log.Printf("Running...")
http.HandleFunc("/", handler)
log.Fatal(http.ListenAndServe("127.0.0.1:8080", nil))
}
// START_1 OMIT
// A SizeReaderAt is a ReaderAt with a Size method.
//
// An io.SectionReader implements SizeReaderAt.
type SizeReaderAt interface {
Size() int64
io.ReaderAt
}
// NewMultiReaderAt is like io.MultiReader but produces a ReaderAt
// (and Size), instead of just a reader.
func NewMultiReaderAt(parts ...SizeReaderAt) SizeReaderAt {
m := &multi{
parts: make([]offsetAndSource, 0, len(parts)),
}
var off int64
for _, p := range parts {
m.parts = append(m.parts, offsetAndSource{off, p})
off += p.Size()
}
m.size = off
return m
}
// END_1 OMIT
type offsetAndSource struct {
off int64
SizeReaderAt
}
type multi struct {
parts []offsetAndSource
size int64
}
func (m *multi) Size() int64 { return m.size }
func (m *multi) ReadAt(p []byte, off int64) (n int, err error) {
wantN := len(p)
// Skip past the requested offset.
skipParts := sort.Search(len(m.parts), func(i int) bool {
// This function returns whether parts[i] will
// contribute any bytes to our output.
part := m.parts[i]
return part.off+part.Size() > off
})
parts := m.parts[skipParts:]
// How far to skip in the first part.
needSkip := off
if len(parts) > 0 {
needSkip -= parts[0].off
}
for len(parts) > 0 && len(p) > 0 {
readP := p
partSize := parts[0].Size()
if int64(len(readP)) > partSize-needSkip {
readP = readP[:partSize-needSkip]
}
pn, err0 := parts[0].ReadAt(readP, needSkip)
if err0 != nil {
return n, err0
}
n += pn
p = p[pn:]
if int64(pn)+needSkip == partSize {
parts = parts[1:]
}
needSkip = 0
}
if n != wantN {
err = io.ErrUnexpectedEOF
}
return
}