diff options
Diffstat (limited to 'libgo/go/net/http/fs.go')
-rw-r--r-- | libgo/go/net/http/fs.go | 56 |
1 files changed, 37 insertions, 19 deletions
diff --git a/libgo/go/net/http/fs.go b/libgo/go/net/http/fs.go index b6bea0dfaad..8b32ca1d0ea 100644 --- a/libgo/go/net/http/fs.go +++ b/libgo/go/net/http/fs.go @@ -105,23 +105,31 @@ func dirList(w ResponseWriter, f File) { // // Note that *os.File implements the io.ReadSeeker interface. func ServeContent(w ResponseWriter, req *Request, name string, modtime time.Time, content io.ReadSeeker) { - size, err := content.Seek(0, os.SEEK_END) - if err != nil { - Error(w, "seeker can't seek", StatusInternalServerError) - return - } - _, err = content.Seek(0, os.SEEK_SET) - if err != nil { - Error(w, "seeker can't seek", StatusInternalServerError) - return + sizeFunc := func() (int64, error) { + size, err := content.Seek(0, os.SEEK_END) + if err != nil { + return 0, errSeeker + } + _, err = content.Seek(0, os.SEEK_SET) + if err != nil { + return 0, errSeeker + } + return size, nil } - serveContent(w, req, name, modtime, size, content) + serveContent(w, req, name, modtime, sizeFunc, content) } +// errSeeker is returned by ServeContent's sizeFunc when the content +// doesn't seek properly. The underlying Seeker's error text isn't +// included in the sizeFunc reply so it's not sent over HTTP to end +// users. +var errSeeker = errors.New("seeker can't seek") + // if name is empty, filename is unknown. (used for mime type, before sniffing) // if modtime.IsZero(), modtime is unknown. // content must be seeked to the beginning of the file. -func serveContent(w ResponseWriter, r *Request, name string, modtime time.Time, size int64, content io.ReadSeeker) { +// The sizeFunc is called at most once. Its error, if any, is sent in the HTTP response. +func serveContent(w ResponseWriter, r *Request, name string, modtime time.Time, sizeFunc func() (int64, error), content io.ReadSeeker) { if checkLastModified(w, r, modtime) { return } @@ -132,16 +140,17 @@ func serveContent(w ResponseWriter, r *Request, name string, modtime time.Time, code := StatusOK - // If Content-Type isn't set, use the file's extension to find it. - ctype := w.Header().Get("Content-Type") - if ctype == "" { + // If Content-Type isn't set, use the file's extension to find it, but + // if the Content-Type is unset explicitly, do not sniff the type. + ctypes, haveType := w.Header()["Content-Type"] + var ctype string + if !haveType { ctype = mime.TypeByExtension(filepath.Ext(name)) if ctype == "" { // read a chunk to decide between utf-8 text and binary - var buf [1024]byte + var buf [sniffLen]byte n, _ := io.ReadFull(content, buf[:]) - b := buf[:n] - ctype = DetectContentType(b) + ctype = DetectContentType(buf[:n]) _, err := content.Seek(0, os.SEEK_SET) // rewind to output whole file if err != nil { Error(w, "seeker can't seek", StatusInternalServerError) @@ -149,6 +158,14 @@ func serveContent(w ResponseWriter, r *Request, name string, modtime time.Time, } } w.Header().Set("Content-Type", ctype) + } else if len(ctypes) > 0 { + ctype = ctypes[0] + } + + size, err := sizeFunc() + if err != nil { + Error(w, err.Error(), StatusInternalServerError) + return } // handle Content-Range header. @@ -160,7 +177,7 @@ func serveContent(w ResponseWriter, r *Request, name string, modtime time.Time, Error(w, err.Error(), StatusRequestedRangeNotSatisfiable) return } - if sumRangesSize(ranges) >= size { + if sumRangesSize(ranges) > size { // The total number of bytes in all the ranges // is larger than the size of the file by // itself, so this is probably an attack, or a @@ -378,7 +395,8 @@ func serveFile(w ResponseWriter, r *Request, fs FileSystem, name string, redirec } // serverContent will check modification time - serveContent(w, r, d.Name(), d.ModTime(), d.Size(), f) + sizeFunc := func() (int64, error) { return d.Size(), nil } + serveContent(w, r, d.Name(), d.ModTime(), sizeFunc, f) } // localRedirect gives a Moved Permanently response. |