fix(handler): normalize root path to fix relative path performance

When root is configured as a relative path (e.g., "./lib/site/"),
filepath.Join normalizes it to "lib/site/" but the original value
was stored in h.root. This caused strings.TrimPrefix in serveFile
to fail, resulting in incorrect path calculations for GzipStatic.

The bug caused every request to attempt opening a non-existent
precompressed file path like "lib/site/lib/site/index.html.gz",
adding an extra failed os.Stat call per request and reducing
performance by ~50%.

Fix by normalizing root with filepath.Clean in NewStaticHandler
and SetRoot to ensure TrimPrefix works correctly.

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
This commit is contained in:
xfy 2026-04-30 13:29:26 +08:00
parent f145a8770e
commit fc586d4ace

View File

@ -82,8 +82,14 @@ type StaticHandler struct {
//
// handler := handler.NewStaticHandler("/var/www", "/", []string{"index.html"}, true)
func NewStaticHandler(root, pathPrefix string, index []string, useSendfile bool) *StaticHandler {
// 规范化 root 路径,确保 TrimPrefix 能正确工作
// filepath.Clean 会去掉 ./ 并规范化路径分隔符
cleanRoot := filepath.Clean(root)
if !strings.HasSuffix(cleanRoot, string(filepath.Separator)) {
cleanRoot += string(filepath.Separator)
}
return &StaticHandler{
root: root,
root: cleanRoot,
pathPrefix: pathPrefix,
index: index,
useSendfile: useSendfile,
@ -136,8 +142,13 @@ func (h *StaticHandler) SetAlias(alias string) {
// 参数:
// - root: 静态文件根目录
func (h *StaticHandler) SetRoot(root string) {
h.root = root
if root != "" {
// 规范化 root 路径
cleanRoot := filepath.Clean(root)
if !strings.HasSuffix(cleanRoot, string(filepath.Separator)) {
cleanRoot += string(filepath.Separator)
}
h.root = cleanRoot
if cleanRoot != "" {
h.alias = ""
}
}