Skip to content

Commit

Permalink
main: rewrite FileServer
Browse files Browse the repository at this point in the history
FileServer now rejects directory listings.
  • Loading branch information
chappjc committed Apr 9, 2019
1 parent d688679 commit c236b00
Showing 1 changed file with 56 additions and 16 deletions.
72 changes: 56 additions & 16 deletions main.go
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,7 @@ import (
"net/http"
_ "net/http/pprof"
"os"
"path"
"path/filepath"
"reflect"
"runtime"
Expand Down Expand Up @@ -771,11 +772,11 @@ func _main(ctx context.Context) error {
http.ServeFile(w, r, "./public/images/favicon.ico")
})
cacheControlMaxAge := int64(cfg.CacheControlMaxAge)
FileServer(webMux, "/js", http.Dir("./public/js"), cacheControlMaxAge)
FileServer(webMux, "/css", http.Dir("./public/css"), cacheControlMaxAge)
FileServer(webMux, "/fonts", http.Dir("./public/fonts"), cacheControlMaxAge)
FileServer(webMux, "/images", http.Dir("./public/images"), cacheControlMaxAge)
FileServer(webMux, "/dist", http.Dir("./public/dist"), cacheControlMaxAge)
FileServer(webMux, "/js", "./public/js", cacheControlMaxAge)
FileServer(webMux, "/css", "./public/css", cacheControlMaxAge)
FileServer(webMux, "/fonts", "./public/fonts", cacheControlMaxAge)
FileServer(webMux, "/images", "./public/images", cacheControlMaxAge)
FileServer(webMux, "/dist", "./public/dist", cacheControlMaxAge)

// HTTP profiler
if cfg.HTTPProfile {
Expand Down Expand Up @@ -1350,21 +1351,60 @@ func listenAndServeProto(ctx context.Context, wg *sync.WaitGroup, listen, proto
}

// FileServer conveniently sets up a http.FileServer handler to serve static
// files from a http.FileSystem.
func FileServer(r chi.Router, path string, root http.FileSystem, cacheControlMaxAge int64) {
if strings.ContainsAny(path, "{}*") {
// files from path on the file system. Directory listings are denied, as are URL
// paths containing "..".
func FileServer(r chi.Router, pathRoot, fsRoot string, cacheControlMaxAge int64) {
if strings.ContainsAny(pathRoot, "{}*") {
panic("FileServer does not permit URL parameters.")
}

fs := http.StripPrefix(path, http.FileServer(root))
// Define a http.HandlerFunc to serve files but not directory indexes.
hf := func(w http.ResponseWriter, r *http.Request) {
// Ensure the path begins with "/".
upath := r.URL.Path
if strings.Contains(upath, "..") {
http.Error(w, http.StatusText(http.StatusForbidden), http.StatusForbidden)
return
}
if !strings.HasPrefix(upath, "/") {
upath = "/" + upath
r.URL.Path = upath
}
// Strip the path prefix and clean the path.
upath = path.Clean(strings.TrimPrefix(upath, pathRoot))

// Deny directory listings (http.ServeFile recognizes index.html and
// attempts to serve the directory contents instead).
if strings.HasSuffix(upath, "/index.html") {
http.NotFound(w, r)
return
}

// Generate the full file system path and test for existence.
fullFilePath := filepath.Join(fsRoot, upath)
fi, err := os.Stat(fullFilePath)
if err != nil {
http.NotFound(w, r)
return
}

// Deny directory listings
if fi.IsDir() {
http.Error(w, http.StatusText(http.StatusForbidden), http.StatusForbidden)
return
}

http.ServeFile(w, r, fullFilePath)
}

if path != "/" && path[len(path)-1] != '/' {
r.Get(path, http.RedirectHandler(path+"/", 301).ServeHTTP)
path += "/"
// For the chi.Mux, make sure a path that ends in "/" and append a "*".
muxRoot := pathRoot
if pathRoot != "/" && pathRoot[len(pathRoot)-1] != '/' {
r.Get(pathRoot, http.RedirectHandler(pathRoot+"/", 301).ServeHTTP)
muxRoot += "/"
}
path += "*"
muxRoot += "*"

r.With(m.CacheControl(cacheControlMaxAge)).Get(path, http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
fs.ServeHTTP(w, r)
}))
// Mount the http.HandlerFunc on the pathRoot.
r.With(m.CacheControl(cacheControlMaxAge)).Get(muxRoot, hf)
}

0 comments on commit c236b00

Please sign in to comment.