From a77d382be0ecdc5d51ad8eea8aaa155bda4aea23 Mon Sep 17 00:00:00 2001 From: uz Date: Fri, 1 Jul 2022 09:58:21 +0900 Subject: [PATCH 1/5] Introduce FS.CompressRoot --- fs.go | 55 +++++++++++++++++++++++++++++++++++++++++-------------- 1 file changed, 41 insertions(+), 14 deletions(-) diff --git a/fs.go b/fs.go index 4bbb3fd671..6d68752e18 100644 --- a/fs.go +++ b/fs.go @@ -276,6 +276,10 @@ type FS struct { // Brotli encoding is disabled by default. CompressBrotli bool + // Path to the compressed root directory to serve files from. If this value + // is empty, Root is used. + CompressRoot string + // Enables byte range requests if set to true. // // Byte range requests are disabled by default. @@ -388,9 +392,7 @@ func (fs *FS) NewRequestHandler() RequestHandler { return fs.h } -func (fs *FS) initRequestHandler() { - root := fs.Root - +func (fs *FS) normalizeRoot(root string) string { // Serve files from the current working directory if Root is empty or if Root is a relative path. if (!fs.AllowEmptyRoot && len(root) == 0) || (len(root) > 0 && !filepath.IsAbs(root)) { path, err := os.Getwd() @@ -406,6 +408,18 @@ func (fs *FS) initRequestHandler() { for len(root) > 0 && root[len(root)-1] == os.PathSeparator { root = root[:len(root)-1] } + return root +} + +func (fs *FS) initRequestHandler() { + root := fs.normalizeRoot(fs.Root) + + compressRoot := fs.CompressRoot + if len(compressRoot) == 0 { + compressRoot = root + } else { + compressRoot = fs.normalizeRoot(compressRoot) + } cacheDuration := fs.CacheDuration if cacheDuration <= 0 { @@ -430,6 +444,7 @@ func (fs *FS) initRequestHandler() { generateIndexPages: fs.GenerateIndexPages, compress: fs.Compress, compressBrotli: fs.CompressBrotli, + compressRoot: compressRoot, pathNotFound: fs.PathNotFound, acceptByteRange: fs.AcceptByteRange, cacheDuration: cacheDuration, @@ -478,6 +493,7 @@ type fsHandler struct { generateIndexPages bool compress bool compressBrotli bool + compressRoot string acceptByteRange bool cacheDuration time.Duration compressedFileSuffixes map[string]string @@ -834,19 +850,19 @@ func (h *fsHandler) handleRequest(ctx *RequestCtx) { filePath := filepath.FromSlash(h.root + pathStr) var err error - ff, err = h.openFSFile(filePath, mustCompress, fileEncoding) + ff, err = h.openFSFile(pathStr, filePath, mustCompress, fileEncoding) if mustCompress && err == errNoCreatePermission { ctx.Logger().Printf("insufficient permissions for saving compressed file for %q. Serving uncompressed file. "+ "Allow write access to the directory with this file in order to improve fasthttp performance", filePath) mustCompress = false - ff, err = h.openFSFile(filePath, mustCompress, fileEncoding) + ff, err = h.openFSFile(pathStr, filePath, mustCompress, fileEncoding) } if err == errDirIndexRequired { if !hasTrailingSlash { ctx.RedirectBytes(append(path, '/'), StatusFound) return } - ff, err = h.openIndexFile(ctx, filePath, mustCompress, fileEncoding) + ff, err = h.openIndexFile(ctx, pathStr, filePath, mustCompress, fileEncoding) if err != nil { ctx.Logger().Printf("cannot open dir index %q: %v", filePath, err) ctx.Error("Directory index is forbidden", StatusForbidden) @@ -1012,10 +1028,11 @@ func ParseByteRange(byteRange []byte, contentLength int) (startPos, endPos int, return startPos, endPos, nil } -func (h *fsHandler) openIndexFile(ctx *RequestCtx, dirPath string, mustCompress bool, fileEncoding string) (*fsFile, error) { +func (h *fsHandler) openIndexFile(ctx *RequestCtx, path, dirPath string, mustCompress bool, fileEncoding string) (*fsFile, error) { for _, indexName := range h.indexNames { - indexFilePath := dirPath + "/" + indexName - ff, err := h.openFSFile(indexFilePath, mustCompress, fileEncoding) + indexPath := path + "/" + indexName + indexFilePath := filepath.Join(dirPath, indexName) + ff, err := h.openFSFile(indexPath, indexFilePath, mustCompress, fileEncoding) if err == nil { return ff, nil } @@ -1130,7 +1147,7 @@ const ( fsMaxCompressibleFileSize = 8 * 1024 * 1024 ) -func (h *fsHandler) compressAndOpenFSFile(filePath string, fileEncoding string) (*fsFile, error) { +func (h *fsHandler) compressAndOpenFSFile(path, filePath string, fileEncoding string) (*fsFile, error) { f, err := os.Open(filePath) if err != nil { return nil, err @@ -1153,7 +1170,17 @@ func (h *fsHandler) compressAndOpenFSFile(filePath string, fileEncoding string) return h.newFSFile(f, fileInfo, false, "") } - compressedFilePath := filePath + h.compressedFileSuffixes[fileEncoding] + var compressedFilePath string + if h.root == h.compressRoot { + compressedFilePath = filePath + } else { + compressedFilePath = filepath.FromSlash(h.compressRoot + path) + if err := os.MkdirAll(filepath.Dir(compressedFilePath), os.ModePerm); err != nil { + return nil, err + } + } + compressedFilePath += h.compressedFileSuffixes[fileEncoding] + absPath, err := filepath.Abs(compressedFilePath) if err != nil { _ = f.Close() @@ -1232,7 +1259,7 @@ func (h *fsHandler) newCompressedFSFile(filePath string, fileEncoding string) (* return h.newFSFile(f, fileInfo, true, fileEncoding) } -func (h *fsHandler) openFSFile(filePath string, mustCompress bool, fileEncoding string) (*fsFile, error) { +func (h *fsHandler) openFSFile(path string, filePath string, mustCompress bool, fileEncoding string) (*fsFile, error) { filePathOriginal := filePath if mustCompress { filePath += h.compressedFileSuffixes[fileEncoding] @@ -1241,7 +1268,7 @@ func (h *fsHandler) openFSFile(filePath string, mustCompress bool, fileEncoding f, err := os.Open(filePath) if err != nil { if mustCompress && os.IsNotExist(err) { - return h.compressAndOpenFSFile(filePathOriginal, fileEncoding) + return h.compressAndOpenFSFile(path, filePathOriginal, fileEncoding) } return nil, err } @@ -1275,7 +1302,7 @@ func (h *fsHandler) openFSFile(filePath string, mustCompress bool, fileEncoding // The compressed file became stale. Re-create it. _ = f.Close() _ = os.Remove(filePath) - return h.compressAndOpenFSFile(filePathOriginal, fileEncoding) + return h.compressAndOpenFSFile(path, filePathOriginal, fileEncoding) } } From 5f432d94344509d6b15804f54db7df79d059d423 Mon Sep 17 00:00:00 2001 From: uz Date: Tue, 5 Jul 2022 09:18:32 +0900 Subject: [PATCH 2/5] Avoid duplicated filepath.FromSlash --- fs.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/fs.go b/fs.go index 6d68752e18..5e1442c944 100644 --- a/fs.go +++ b/fs.go @@ -1174,7 +1174,7 @@ func (h *fsHandler) compressAndOpenFSFile(path, filePath string, fileEncoding st if h.root == h.compressRoot { compressedFilePath = filePath } else { - compressedFilePath = filepath.FromSlash(h.compressRoot + path) + compressedFilePath = h.compressRoot + path if err := os.MkdirAll(filepath.Dir(compressedFilePath), os.ModePerm); err != nil { return nil, err } From 3dd25f3a3bc0b5693d01d2abcd99b22d88eeefe5 Mon Sep 17 00:00:00 2001 From: uz Date: Thu, 7 Jul 2022 15:14:29 +0900 Subject: [PATCH 3/5] Introduce filePathToCompressed --- fs.go | 38 ++++++++++++++++++++++++-------------- 1 file changed, 24 insertions(+), 14 deletions(-) diff --git a/fs.go b/fs.go index 5e1442c944..6f881b2d5c 100644 --- a/fs.go +++ b/fs.go @@ -796,6 +796,20 @@ func cleanCacheNolock(cache map[string]*fsFile, pendingFiles, filesToRelease []* return pendingFiles, filesToRelease } +func (h *fsHandler) pathToFilePath(path string) string { + return h.root + filepath.FromSlash(path) +} + +func (h *fsHandler) filePathToCompressed(filePath string) string { + if h.root == h.compressRoot { + return filePath + } + if !strings.HasPrefix(filePath, h.root) { + return filePath + } + return h.compressRoot + filePath[len(h.root):] +} + func (h *fsHandler) handleRequest(ctx *RequestCtx) { var path []byte if h.pathRewrite != nil { @@ -847,15 +861,15 @@ func (h *fsHandler) handleRequest(ctx *RequestCtx) { if !ok { pathStr := string(path) - filePath := filepath.FromSlash(h.root + pathStr) + filePath := h.pathToFilePath(pathStr) var err error - ff, err = h.openFSFile(pathStr, filePath, mustCompress, fileEncoding) + ff, err = h.openFSFile(filePath, mustCompress, fileEncoding) if mustCompress && err == errNoCreatePermission { ctx.Logger().Printf("insufficient permissions for saving compressed file for %q. Serving uncompressed file. "+ "Allow write access to the directory with this file in order to improve fasthttp performance", filePath) mustCompress = false - ff, err = h.openFSFile(pathStr, filePath, mustCompress, fileEncoding) + ff, err = h.openFSFile(filePath, mustCompress, fileEncoding) } if err == errDirIndexRequired { if !hasTrailingSlash { @@ -1030,9 +1044,8 @@ func ParseByteRange(byteRange []byte, contentLength int) (startPos, endPos int, func (h *fsHandler) openIndexFile(ctx *RequestCtx, path, dirPath string, mustCompress bool, fileEncoding string) (*fsFile, error) { for _, indexName := range h.indexNames { - indexPath := path + "/" + indexName indexFilePath := filepath.Join(dirPath, indexName) - ff, err := h.openFSFile(indexPath, indexFilePath, mustCompress, fileEncoding) + ff, err := h.openFSFile(indexFilePath, mustCompress, fileEncoding) if err == nil { return ff, nil } @@ -1147,7 +1160,7 @@ const ( fsMaxCompressibleFileSize = 8 * 1024 * 1024 ) -func (h *fsHandler) compressAndOpenFSFile(path, filePath string, fileEncoding string) (*fsFile, error) { +func (h *fsHandler) compressAndOpenFSFile(filePath string, fileEncoding string) (*fsFile, error) { f, err := os.Open(filePath) if err != nil { return nil, err @@ -1170,11 +1183,8 @@ func (h *fsHandler) compressAndOpenFSFile(path, filePath string, fileEncoding st return h.newFSFile(f, fileInfo, false, "") } - var compressedFilePath string - if h.root == h.compressRoot { - compressedFilePath = filePath - } else { - compressedFilePath = h.compressRoot + path + compressedFilePath := h.filePathToCompressed(filePath) + if compressedFilePath != filePath { if err := os.MkdirAll(filepath.Dir(compressedFilePath), os.ModePerm); err != nil { return nil, err } @@ -1259,7 +1269,7 @@ func (h *fsHandler) newCompressedFSFile(filePath string, fileEncoding string) (* return h.newFSFile(f, fileInfo, true, fileEncoding) } -func (h *fsHandler) openFSFile(path string, filePath string, mustCompress bool, fileEncoding string) (*fsFile, error) { +func (h *fsHandler) openFSFile(filePath string, mustCompress bool, fileEncoding string) (*fsFile, error) { filePathOriginal := filePath if mustCompress { filePath += h.compressedFileSuffixes[fileEncoding] @@ -1268,7 +1278,7 @@ func (h *fsHandler) openFSFile(path string, filePath string, mustCompress bool, f, err := os.Open(filePath) if err != nil { if mustCompress && os.IsNotExist(err) { - return h.compressAndOpenFSFile(path, filePathOriginal, fileEncoding) + return h.compressAndOpenFSFile(filePathOriginal, fileEncoding) } return nil, err } @@ -1302,7 +1312,7 @@ func (h *fsHandler) openFSFile(path string, filePath string, mustCompress bool, // The compressed file became stale. Re-create it. _ = f.Close() _ = os.Remove(filePath) - return h.compressAndOpenFSFile(path, filePathOriginal, fileEncoding) + return h.compressAndOpenFSFile(filePathOriginal, fileEncoding) } } From fa6652bf9e2714060e580e92b4954f0673ba15a0 Mon Sep 17 00:00:00 2001 From: uz Date: Fri, 8 Jul 2022 17:14:46 +0900 Subject: [PATCH 4/5] Revert openIndexFile manually --- fs.go | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/fs.go b/fs.go index 6f881b2d5c..3810fa263e 100644 --- a/fs.go +++ b/fs.go @@ -876,7 +876,7 @@ func (h *fsHandler) handleRequest(ctx *RequestCtx) { ctx.RedirectBytes(append(path, '/'), StatusFound) return } - ff, err = h.openIndexFile(ctx, pathStr, filePath, mustCompress, fileEncoding) + ff, err = h.openIndexFile(ctx, filePath, mustCompress, fileEncoding) if err != nil { ctx.Logger().Printf("cannot open dir index %q: %v", filePath, err) ctx.Error("Directory index is forbidden", StatusForbidden) @@ -1042,9 +1042,9 @@ func ParseByteRange(byteRange []byte, contentLength int) (startPos, endPos int, return startPos, endPos, nil } -func (h *fsHandler) openIndexFile(ctx *RequestCtx, path, dirPath string, mustCompress bool, fileEncoding string) (*fsFile, error) { +func (h *fsHandler) openIndexFile(ctx *RequestCtx, dirPath string, mustCompress bool, fileEncoding string) (*fsFile, error) { for _, indexName := range h.indexNames { - indexFilePath := filepath.Join(dirPath, indexName) + indexFilePath := dirPath + "/" + indexName ff, err := h.openFSFile(indexFilePath, mustCompress, fileEncoding) if err == nil { return ff, nil From 38b41a8133e7ae2c3cc0744011727482c50d033d Mon Sep 17 00:00:00 2001 From: uz Date: Sun, 10 Jul 2022 14:02:22 +0900 Subject: [PATCH 5/5] Join root and path, and then calls filepath.FromSlash --- fs.go | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/fs.go b/fs.go index 3810fa263e..8491864565 100644 --- a/fs.go +++ b/fs.go @@ -797,7 +797,7 @@ func cleanCacheNolock(cache map[string]*fsFile, pendingFiles, filesToRelease []* } func (h *fsHandler) pathToFilePath(path string) string { - return h.root + filepath.FromSlash(path) + return filepath.FromSlash(h.root + path) } func (h *fsHandler) filePathToCompressed(filePath string) string { @@ -807,7 +807,7 @@ func (h *fsHandler) filePathToCompressed(filePath string) string { if !strings.HasPrefix(filePath, h.root) { return filePath } - return h.compressRoot + filePath[len(h.root):] + return filepath.FromSlash(h.compressRoot + filePath[len(h.root):]) } func (h *fsHandler) handleRequest(ctx *RequestCtx) {