diff --git a/fs.go b/fs.go index 549cf3d..d924848 100644 --- a/fs.go +++ b/fs.go @@ -99,10 +99,13 @@ func ServeFile(ctx *RequestCtx, path string) { rootFSOnce.Do(func() { rootFSHandler = rootFS.NewRequestHandler() }) - if len(path) == 0 || path[0] != '/' { + + if len(path) == 0 || !filepath.IsAbs(path) { // extend relative path to absolute path - hasTrailingSlash := len(path) > 0 && path[len(path)-1] == '/' + hasTrailingSlash := len(path) > 0 && (path[len(path)-1] == '/' || path[len(path)-1] == '\\') + var err error + path = filepath.FromSlash(path) if path, err = filepath.Abs(path); err != nil { ctx.Logger().Printf("cannot resolve path %q to absolute file path: %v", path, err) ctx.Error("Internal Server Error", StatusInternalServerError) @@ -112,6 +115,11 @@ func ServeFile(ctx *RequestCtx, path string) { path += "/" } } + + // convert the path to forward slashes regardless the OS in order to set the URI properly + // the handler will convert back to OS path separator before opening the file + path = filepath.ToSlash(path) + ctx.Request.SetRequestURI(path) rootFSHandler(ctx) } @@ -119,7 +127,7 @@ func ServeFile(ctx *RequestCtx, path string) { var ( rootFSOnce sync.Once rootFS = &FS{ - Root: "/", + Root: "", GenerateIndexPages: true, Compress: true, CompressBrotli: true, @@ -376,13 +384,22 @@ func (fs *FS) NewRequestHandler() RequestHandler { func (fs *FS) initRequestHandler() { root := fs.Root - // serve files from the current working directory if root is empty - if len(root) == 0 { - root = "." + // rootFS' handleRequest will do special treatment of absolute and relative paths + if fs != rootFS { + // serve files from the current working directory if root is empty + if len(root) == 0 || !filepath.IsAbs(root) { + path, err := os.Getwd() + if err != nil { + path = "." + } + root = path + "/" + root + } + // convert the root directory slashes to the native format + root = filepath.FromSlash(root) } // strip trailing slashes from the root path - for len(root) > 0 && root[len(root)-1] == '/' { + for len(root) > 0 && root[len(root)-1] == os.PathSeparator { root = root[:len(root)-1] } @@ -495,10 +512,10 @@ func (ff *fsFile) NewReader() (io.Reader, error) { } return r, err } - return ff.smallFileReader(), nil + return ff.smallFileReader() } -func (ff *fsFile) smallFileReader() io.Reader { +func (ff *fsFile) smallFileReader() (io.Reader, error) { v := ff.h.smallFileReaderPool.Get() if v == nil { v = &fsSmallFileReader{} @@ -507,9 +524,9 @@ func (ff *fsFile) smallFileReader() io.Reader { r.ff = ff r.endPos = ff.contentLength if r.startPos > 0 { - panic("BUG: fsSmallFileReader with non-nil startPos found in the pool") + return nil, errors.New("bug: fsSmallFileReader with non-nil startPos found in the pool") } - return r + return r, nil } // files bigger than this size are sent with sendfile @@ -521,7 +538,7 @@ func (ff *fsFile) isBig() bool { func (ff *fsFile) bigFileReader() (io.Reader, error) { if ff.f == nil { - panic("BUG: ff.f must be non-nil in bigFileReader") + return nil, errors.New("bug: ff.f must be non-nil in bigFileReader") } var r io.Reader @@ -551,12 +568,12 @@ func (ff *fsFile) bigFileReader() (io.Reader, error) { func (ff *fsFile) Release() { if ff.f != nil { - ff.f.Close() + _ = ff.f.Close() if ff.isBig() { ff.bigFilesLock.Lock() for _, r := range ff.bigFiles { - r.f.Close() + _ = r.f.Close() } ff.bigFilesLock.Unlock() } @@ -597,7 +614,7 @@ func (r *bigFileReader) Read(p []byte) (int, error) { func (r *bigFileReader) WriteTo(w io.Writer) (int64, error) { if rf, ok := w.(io.ReaderFrom); ok { - // fast path. Senfile must be triggered + // fast path. Send file must be triggered return rf.ReadFrom(r.r) } @@ -609,16 +626,17 @@ func (r *bigFileReader) Close() error { r.r = r.f n, err := r.f.Seek(0, 0) if err == nil { - if n != 0 { - panic("BUG: File.Seek(0,0) returned (non-zero, nil)") + if n == 0 { + ff := r.ff + ff.bigFilesLock.Lock() + ff.bigFiles = append(ff.bigFiles, r) + ff.bigFilesLock.Unlock() + } else { + _ = r.f.Close() + err = errors.New("bug: File.Seek(0,0) returned (non-zero, nil)") } - - ff := r.ff - ff.bigFilesLock.Lock() - ff.bigFiles = append(ff.bigFiles, r) - ff.bigFilesLock.Unlock() } else { - r.f.Close() + _ = r.f.Close() } r.ff.decReadersCount() return err @@ -696,7 +714,7 @@ func (r *fsSmallFileReader) WriteTo(w io.Writer) (int64, error) { nw, errw := w.Write(buf[:n]) curPos += nw if errw == nil && nw != n { - panic("BUG: Write(p) returned (n, nil), where n != len(p)") + errw = errors.New("bug: Write(p) returned (n, nil), where n != len(p)") } if err == nil { err = errw @@ -809,7 +827,8 @@ func (h *fsHandler) handleRequest(ctx *RequestCtx) { if !ok { pathStr := string(path) - filePath := h.root + pathStr + filePath := filepath.FromSlash(h.root + pathStr) + var err error ff, err = h.openFSFile(filePath, mustCompress, fileEncoding) if mustCompress && err == errNoCreatePermission { @@ -888,14 +907,14 @@ func (h *fsHandler) handleRequest(ctx *RequestCtx) { if len(byteRange) > 0 { startPos, endPos, err := ParseByteRange(byteRange, contentLength) if err != nil { - r.(io.Closer).Close() + _ = r.(io.Closer).Close() ctx.Logger().Printf("cannot parse byte range %q for path=%q: %v", byteRange, path, err) ctx.Error("Range Not Satisfiable", StatusRequestedRangeNotSatisfiable) return } if err = r.(byteRangeUpdater).UpdateByteRange(startPos, endPos); err != nil { - r.(io.Closer).Close() + _ = r.(io.Closer).Close() ctx.Logger().Printf("cannot seek byte range %q for path=%q: %v", byteRange, path, err) ctx.Error("Internal Server Error", StatusInternalServerError) return @@ -1017,16 +1036,16 @@ func (h *fsHandler) createDirIndex(base *URI, dirPath string, mustCompress bool, w := &bytebufferpool.ByteBuffer{} basePathEscaped := html.EscapeString(string(base.Path())) - fmt.Fprintf(w, "