gofiber / fiber

⚡️ Express inspired web framework written in Go
https://gofiber.io
MIT License
33.89k stars 1.67k forks source link

🤗 [Question]: The fiber static resource handler will response with 200 OK but not body and content length even if resource not found actually. #2352

Closed benz9527 closed 1 year ago

benz9527 commented 1 year ago

Question Description

b8c959e2387e40fc3df25b896c53672f

93b4f657bf2899ab7d69abd82bdbfc26

4019dd49379f3ca68fd38b4ea5a9bf0a

I read the registerStatic

func (app *App) registerStatic(prefix, root string, config ...Static) Router {
    // For security we want to restrict to the current work directory.
    if root == "" {
        root = "."
    }
    // Cannot have an empty prefix
    if prefix == "" {
        prefix = "/"
    }
    // Prefix always start with a '/' or '*'
    if prefix[0] != '/' {
        prefix = "/" + prefix
    }
    // in case sensitive routing, all to lowercase
    if !app.config.CaseSensitive {
        prefix = utils.ToLower(prefix)
    }
    // Strip trailing slashes from the root path
    if len(root) > 0 && root[len(root)-1] == '/' {
        root = root[:len(root)-1]
    }
    // Is prefix a direct wildcard?
    isStar := prefix == "/*"
    // Is prefix a root slash?
    isRoot := prefix == "/"
    // Is prefix a partial wildcard?
    if strings.Contains(prefix, "*") {
        // /john* -> /john
        isStar = true
        prefix = strings.Split(prefix, "*")[0]
        // Fix this later
    }
    prefixLen := len(prefix)
    if prefixLen > 1 && prefix[prefixLen-1:] == "/" {
        // /john/ -> /john
        prefixLen--
        prefix = prefix[:prefixLen]
    }
    const cacheDuration = 10 * time.Second
    // Fileserver settings
    fs := &fasthttp.FS{
        Root:                 root,
        AllowEmptyRoot:       true,
        GenerateIndexPages:   false,
        AcceptByteRange:      false,
        Compress:             false,
        CompressedFileSuffix: app.config.CompressedFileSuffix,
        CacheDuration:        cacheDuration,
        IndexNames:           []string{"index.html"},
        PathRewrite: func(fctx *fasthttp.RequestCtx) []byte {
            path := fctx.Path()
            if len(path) >= prefixLen {
                if isStar && app.getString(path[0:prefixLen]) == prefix {
                    path = append(path[0:0], '/')
                } else {
                    path = path[prefixLen:]
                    if len(path) == 0 || path[len(path)-1] != '/' {
                        path = append(path, '/')
                    }
                }
            }
            if len(path) > 0 && path[0] != '/' {
                path = append([]byte("/"), path...)
            }
            return path
        },
        PathNotFound: func(fctx *fasthttp.RequestCtx) {
            fctx.Response.SetStatusCode(StatusNotFound)
        },
    }

    // Set config if provided
    var cacheControlValue string
    var modifyResponse Handler
    if len(config) > 0 {
        maxAge := config[0].MaxAge
        if maxAge > 0 {
            cacheControlValue = "public, max-age=" + strconv.Itoa(maxAge)
        }
        fs.CacheDuration = config[0].CacheDuration
        fs.Compress = config[0].Compress
        fs.AcceptByteRange = config[0].ByteRange
        fs.GenerateIndexPages = config[0].Browse
        if config[0].Index != "" {
            fs.IndexNames = []string{config[0].Index}
        }
        modifyResponse = config[0].ModifyResponse
    }
    fileHandler := fs.NewRequestHandler()
    handler := func(c *Ctx) error {
        // Don't execute middleware if Next returns true
        if len(config) != 0 && config[0].Next != nil && config[0].Next(c) {
            return c.Next()
        }
        // Serve file
        fileHandler(c.fasthttp)
        // Sets the response Content-Disposition header to attachment if the Download option is true
        if len(config) > 0 && config[0].Download {
            c.Attachment()
        }
        // Return request if found and not forbidden
        status := c.fasthttp.Response.StatusCode() // <====== actually here will catch the 404 status code, why the handler logic not stop and return?
        if status != StatusNotFound && status != StatusForbidden {
            if len(cacheControlValue) > 0 {
                c.fasthttp.Response.Header.Set(HeaderCacheControl, cacheControlValue)
            }
            if modifyResponse != nil {
                return modifyResponse(c)
            }
            return nil
        }
        // Reset response to default
        c.fasthttp.SetContentType("") // Issue #420
        c.fasthttp.Response.SetStatusCode(StatusOK)
        c.fasthttp.Response.SetBodyString("")
        // Next middleware 
        return c.Next()
    }

    // Create route metadata without pointer
    route := Route{
        // Router booleans
        use:  true,
        root: isRoot,
        path: prefix,
        // Public data
        Method:   MethodGet,
        Path:     prefix,
        Handlers: []Handler{handler},
    }
    // Increment global handler count
    atomic.AddUint32(&app.handlersCount, 1)
    // Add route to stack
    app.addRoute(MethodGet, &route)
    // Add HEAD route
    app.addRoute(MethodHead, &route)
    return app
}

fiber version: v2.42.0

I have checked the issues, and found an issue related with this code snippet (#1099), though this issue is not same as my question actually.

Code Snippet (optional)

var (
    extBlackList = []string{
        ".scss", ".sass",
    }
)

func staticAssetsGroups(app *Fiber.App) *Fiber.App {
    abs := ""
    var themePath = "/theme"
    conf := Fiber.Static{
        Compress:      true,
        Browse:        false,
        Download:      false,
        Index:         "",
        CacheDuration: 10 * time.Second,
        MaxAge:        3600,
        ModifyResponse: func(ctx *Fiber.Ctx) error {
            notFound := len(ctx.Get(Fiber.HeaderContentType)) <= 0 &&
                len(ctx.Body()) <= 0
            if notFound {
                return ctx.SendStatus(http.StatusNotFound)
            }
            return nil
        },
        Next: func(ctx *Fiber.Ctx) bool {
            url := ctx.OriginalURL()
            for _, v := range extBlackList {
                if strings.HasSuffix(url, v) {
                    ctx.Status(http.StatusNotFound)
                    return true
                }
            }
            return false
        },
    }
    app.Static(themePath+"/js", path.Join(abs, "theme/js"), conf)
    app.Static(themePath+"/scss", path.Join(abs, "theme/scss"), conf)
    app.Static(themePath+"/images", path.Join(abs, "theme/images"), conf)
    app.Static(themePath+"/scss", path.Join(abs, "theme/scss"), conf)
    // Single static asset file.
    app.Get("/sw.min.js", ctrl.GetAssets(path.Join(abs, "theme/sw.min.js")))
    app.Get("/halt.html", ctrl.GetAssets(path.Join(abs, "theme/halt.html")))
    return app
}

Checklist:

ReneWerner87 commented 1 year ago

what is the status of the response at the time when you execute modifiy reponse?

can you output that inside please

ctx.fasthttp.Response.StatusCode()

and i would suggest that you do not define the same thing twice for scss

benz9527 commented 1 year ago

what is the status of the response at the time when you execute modifiy reponse?

can you output that inside please

ctx.fasthttp.Response.StatusCode()

and i would suggest that you do not define the same thing twice for scss

Actually, I just try to request a no exists asset name search.abc under the path /theme/scss.

Snipaste_2023-03-02_21-09-00

Skyenought commented 1 year ago

Why not use the filesystem middleware to handle this batch of files?

And I think you can check if there is a usage error, and if there is no solution, you are welcome to provide a github repo of the code snippet used to reproduce the problem to see what went wrong

为什么不使用filesystem中间件来处理这批文件呢?

并且我觉得你可以检查一下是否用法出现了错误, 如果实在没有办法解决, 欢迎你提供一个用于复现问题的代码片段的 github repo 用于查看是哪里出了问题

benz9527 commented 1 year ago

The problem has nothing to do with fasthttp.FS, it's just that I signed up for global error handling, but haven't started implementing the logic yet, so it permits all errors. I made a simple version of registerStatic that also solved the problem.

这个问题其实和 fasthttp.FS 没啥关系,主要是我之前注册了全局的错误处理,但是还没有开始实现具体的逻辑,导致它放行了所有的错误。我做了个猴版的 registerStatic 函数,也能解决问题。

Closed.