}
w.Header().Set("Accept-Ranges", "bytes")
- if w.Header().Get("Content-Encoding") == "" {
- w.Header().Set("Content-Length", strconv.FormatInt(sendSize, 10))
- }
+ w.Header().Set("Content-Length", strconv.FormatInt(sendSize, 10))
w.WriteHeader(code)
serveFile(w, r, Dir(dir), file, false)
}
+// ServeFileFS replies to the request with the contents
+// of the named file or directory from the file system fsys.
+//
+// If the provided file or directory name is a relative path, it is
+// interpreted relative to the current directory and may ascend to
+// parent directories. If the provided name is constructed from user
+// input, it should be sanitized before calling ServeFile.
+//
+// As a precaution, ServeFile will reject requests where r.URL.Path
+// contains a ".." path element; this protects against callers who
+// might unsafely use filepath.Join on r.URL.Path without sanitizing
+// it and then use that filepath.Join result as the name argument.
+//
+// As another special case, ServeFile redirects any request where r.URL.Path
+// ends in "/index.html" to the same path, without the final
+// "index.html". To avoid such redirects either modify the path or
+// use ServeContent.
+//
+// Outside of those two special cases, ServeFile does not use
+// r.URL.Path for selecting the file or directory to serve; only the
+// file or directory provided in the name argument is used.
+func ServeFileFS(w ResponseWriter, r *Request, fsys fs.FS, name string) {
+ if containsDotDot(r.URL.Path) {
+ // Too many programs use r.URL.Path to construct the argument to
+ // serveFile. Reject the request under the assumption that happened
+ // here and ".." may not be wanted.
+ // Note that name might not contain "..", for example if code (still
+ // incorrectly) used filepath.Join(myDir, r.URL.Path).
+ Error(w, "invalid URL path", StatusBadRequest)
+ return
+ }
+ serveFile(w, r, FS(fsys), name, false)
+}
+
func containsDotDot(v string) bool {
if !strings.Contains(v, "..") {
return false
//
// http.Handle("/", http.FileServer(http.Dir("/tmp")))
//
-// To use an fs.FS implementation, use http.FS to convert it:
-//
-// http.Handle("/", http.FileServer(http.FS(fsys)))
+// To use an fs.FS implementation, use http.FileServerFS instead.
func FileServer(root FileSystem) Handler {
return &fileHandler{root}
}
-func (f *fileHandler) ServeHTTP(w ResponseWriter, r *Request) {
- const options = MethodOptions + ", " + MethodGet + ", " + MethodHead
-
- switch r.Method {
- case MethodGet, MethodHead:
- if !strings.HasPrefix(r.URL.Path, "/") {
- r.URL.Path = "/" + r.URL.Path
- }
- serveFile(w, r, f.root, path.Clean(r.URL.Path), true)
-
- case MethodOptions:
- w.Header().Set("Allow", options)
+// FileServerFS returns a handler that serves HTTP requests
+// with the contents of the file system fsys.
+//
+// As a special case, the returned file server redirects any request
+// ending in "/index.html" to the same path, without the final
+// "index.html".
+//
+// http.Handle("/", http.FileServerFS(fsys))
+func FileServerFS(root fs.FS) Handler {
+ return FileServer(FS(root))
+}
- default:
- w.Header().Set("Allow", options)
- Error(w, "read-only", StatusMethodNotAllowed)
+func (f *fileHandler) ServeHTTP(w ResponseWriter, r *Request) {
+ upath := r.URL.Path
+ if !strings.HasPrefix(upath, "/") {
+ upath = "/" + upath
+ r.URL.Path = upath
}
+ serveFile(w, r, f.root, path.Clean(upath), true)
}
// httpRange specifies the byte range to be sent to the client.