diff --git a/.travis.yml b/.travis.yml index 16acdc9c..31edb442 100644 --- a/.travis.yml +++ b/.travis.yml @@ -9,7 +9,6 @@ go: env: - GO111MODULE=on install: - - go get -v golang.org/x/lint/golint + #- go get -v golang.org/x/lint/golint script: -# - golint -set_exit_status ./... - - go test ./... + - go test diff --git a/app.go b/app.go index 189fe663..e1c83f3f 100644 --- a/app.go +++ b/app.go @@ -25,7 +25,7 @@ import ( ) // Version of Fiber -const Version = "1.8.3" +const Version = "1.8.32" type ( // App denotes the Fiber application. @@ -115,9 +115,28 @@ func (app *App) Group(prefix string, handlers ...func(*Ctx)) *Group { } } +// Static represents settings for serving static files +type Static struct { + // Transparently compresses responses if set to true + // This works differently than the github.com/gofiber/compression middleware + // The server tries minimizing CPU usage by caching compressed files. + // It adds ".fiber.gz" suffix to the original file name. + // Optional. Default value false + Compress bool + // Enables byte range requests if set to true. + // Optional. Default value false + ByteRange bool + // Enable directory browsing. + // Optional. Default value false. + Browse bool + // Index file for serving a directory. + // Optional. Default value "index.html". + Index string +} + // Static : https://fiber.wiki/application#static -func (app *App) Static(prefix, root string) *App { - app.registerStatic(prefix, root) +func (app *App) Static(prefix, root string, config ...Static) *App { + app.registerStatic(prefix, root, config...) return app } diff --git a/group.go b/group.go index 4a2e2f12..208838bc 100644 --- a/group.go +++ b/group.go @@ -29,9 +29,9 @@ func (grp *Group) Group(prefix string, handlers ...func(*Ctx)) *Group { } // Static : https://fiber.wiki/application#static -func (grp *Group) Static(prefix, root string) *Group { +func (grp *Group) Static(prefix, root string, config ...Static) *Group { prefix = groupPaths(grp.prefix, prefix) - grp.app.registerStatic(prefix, root) + grp.app.registerStatic(prefix, root, config...) return grp } diff --git a/router.go b/router.go index c0843a47..b7946f7e 100644 --- a/router.go +++ b/router.go @@ -6,10 +6,9 @@ package fiber import ( "log" - "os" - "path/filepath" "regexp" "strings" + "time" websocket "github.com/fasthttp/websocket" fasthttp "github.com/valyala/fasthttp" @@ -259,67 +258,88 @@ func (app *App) registerWebSocket(method, path string, handle func(*Ctx)) { }) } -func (app *App) registerStatic(prefix, root string) { +func (app *App) registerStatic(prefix, root string, config ...Static) { // Cannot have an empty prefix if prefix == "" { prefix = "/" } - // prefix always start with a '/' or '*' + // Prefix always start with a '/' or '*' if prefix[0] != '/' && prefix[0] != '*' { prefix = "/" + prefix } + // Match anything + var wildcard = false + if prefix == "*" || prefix == "/*" { + wildcard = true + prefix = "/" + } // Case sensitive routing, all to lowercase if !app.Settings.CaseSensitive { prefix = strings.ToLower(prefix) } - - var isStar = prefix == "*" || prefix == "/*" - - files := map[string]string{} - // Clean root path - root = filepath.Clean(root) - // Check if root exist and is accessible - if _, err := os.Stat(root); err != nil { - log.Fatalf("%s", err) + // For security we want to restrict to the current work directory. + if len(root) == 0 { + root = "." } - // Store path url and file paths in map - if err := filepath.Walk(root, func(path string, info os.FileInfo, err error) error { - if !info.IsDir() { - url := "*" - if !isStar { - // /css/style.css: static/css/style.css - url = filepath.Join(prefix, strings.Replace(path, root, "", 1)) - } - // \static\css: /static/css - url = filepath.ToSlash(url) - files[url] = path - if filepath.Base(path) == "index.html" || filepath.Base(path) == "index.htm" { - files[filepath.ToSlash(filepath.Dir(url))] = path - } + // Strip trailing slashes from the root path + if len(root) > 0 && root[len(root)-1] == '/' { + root = root[:len(root)-1] + } + // isSlash ? + var isSlash = prefix == "/" + if strings.Contains(prefix, "*") { + wildcard = true + prefix = strings.Split(prefix, "*")[0] + } + var stripper = len(prefix) + if isSlash { + stripper = 0 + } + // Fileserver settings + fs := &fasthttp.FS{ + Root: root, + GenerateIndexPages: false, + AcceptByteRange: false, + Compress: false, + CompressedFileSuffix: ".fiber.gz", + CacheDuration: 10 * time.Second, + IndexNames: []string{"index.html"}, + PathRewrite: fasthttp.NewPathPrefixStripper(stripper), + PathNotFound: func(ctx *fasthttp.RequestCtx) { + ctx.Response.SetStatusCode(404) + ctx.Response.SetBodyString("Not Found") + }, + } + // Set config if provided + if len(config) > 0 { + 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} } - return err - }); err != nil { - log.Fatalf("%s", err) } - compress := app.Settings.Compression + fileHandler := fs.NewRequestHandler() app.routes = append(app.routes, &Route{ isMiddleware: true, - isStar: isStar, + isSlash: isSlash, Method: "*", Path: prefix, HandleCtx: func(c *Ctx) { - // Only allow GET & HEAD methods + // Only handle GET & HEAD methods if c.method == "GET" || c.method == "HEAD" { - path := "*" - if !isStar { - path = c.path + // Do stuff + if wildcard { + c.Fasthttp.Request.SetRequestURI(prefix) } - file := files[path] - if file != "" { - c.SendFile(file, compress) + // Serve file + fileHandler(c.Fasthttp) + // End response when file is found + if c.Fasthttp.Response.StatusCode() != 404 { return } } + // Bye c.Next() }, })