1
0
mirror of https://github.com/golang/go synced 2024-09-29 04:14:27 -06:00

net/http: map FS Open errors just like Dir

When an http.FileServer is given a path like file1/file2 where file1
exists but file2 does not, the proper HTTP status should be
NotFound. Some OSes return a "not a directory" error instead, so this
must be mapped to NotFound.

That mapping was already being done for the Dir FileSystem
implementation, as discussed in #18984. But it wasn't for the
FS implementation.

This CL does the same mapping for FS, by generalizing the function
that did it for Dir.

Fixes #49552

Change-Id: I61d6aa8ef101158e9674707d44e653f5dedbd040
Reviewed-on: https://go-review.googlesource.com/c/go/+/376874
Trust: Jonathan Amsterdam <jba@google.com>
Run-TryBot: Jonathan Amsterdam <jba@google.com>
Reviewed-by: Emmanuel Odeke <emmanuel@orijtech.com>
TryBot-Result: Gopher Robot <gobot@golang.org>
Reviewed-by: Ian Lance Taylor <iant@golang.org>
This commit is contained in:
Jonathan Amsterdam 2022-01-08 09:18:24 -05:00 committed by Emmanuel Odeke
parent 2639f2f79b
commit 6df0957060
2 changed files with 25 additions and 14 deletions

View File

@ -42,20 +42,20 @@ import (
// An empty Dir is treated as ".".
type Dir string
// mapDirOpenError maps the provided non-nil error from opening name
// mapOpenError maps the provided non-nil error from opening name
// to a possibly better non-nil error. In particular, it turns OS-specific errors
// about opening files in non-directories into fs.ErrNotExist. See Issue 18984.
func mapDirOpenError(originalErr error, name string) error {
// about opening files in non-directories into fs.ErrNotExist. See Issues 18984 and 49552.
func mapOpenError(originalErr error, name string, sep rune, stat func(string) (fs.FileInfo, error)) error {
if errors.Is(originalErr, fs.ErrNotExist) || errors.Is(originalErr, fs.ErrPermission) {
return originalErr
}
parts := strings.Split(name, string(filepath.Separator))
parts := strings.Split(name, string(sep))
for i := range parts {
if parts[i] == "" {
continue
}
fi, err := os.Stat(strings.Join(parts[:i+1], string(filepath.Separator)))
fi, err := stat(strings.Join(parts[:i+1], string(sep)))
if err != nil {
return originalErr
}
@ -79,7 +79,7 @@ func (d Dir) Open(name string) (File, error) {
fullName := filepath.Join(dir, filepath.FromSlash(path.Clean("/"+name)))
f, err := os.Open(fullName)
if err != nil {
return nil, mapDirOpenError(err, fullName)
return nil, mapOpenError(err, fullName, filepath.Separator, os.Stat)
}
return f, nil
}
@ -759,7 +759,9 @@ func (f ioFS) Open(name string) (File, error) {
}
file, err := f.fsys.Open(name)
if err != nil {
return nil, err
return nil, mapOpenError(err, name, '/', func(path string) (fs.FileInfo, error) {
return fs.Stat(f.fsys, path)
})
}
return ioFile{file}, nil
}

View File

@ -1244,10 +1244,19 @@ func TestLinuxSendfileChild(*testing.T) {
}
}
// Issue 18984: tests that requests for paths beyond files return not-found errors
// Issues 18984, 49552: tests that requests for paths beyond files return not-found errors
func TestFileServerNotDirError(t *testing.T) {
defer afterTest(t)
ts := httptest.NewServer(FileServer(Dir("testdata")))
t.Run("Dir", func(t *testing.T) {
testFileServerNotDirError(t, func(path string) FileSystem { return Dir(path) })
})
t.Run("FS", func(t *testing.T) {
testFileServerNotDirError(t, func(path string) FileSystem { return FS(os.DirFS(path)) })
})
}
func testFileServerNotDirError(t *testing.T, newfs func(string) FileSystem) {
ts := httptest.NewServer(FileServer(newfs("testdata")))
defer ts.Close()
res, err := Get(ts.URL + "/index.html/not-a-file")
@ -1259,9 +1268,9 @@ func TestFileServerNotDirError(t *testing.T) {
t.Errorf("StatusCode = %v; want 404", res.StatusCode)
}
test := func(name string, dir Dir) {
test := func(name string, fsys FileSystem) {
t.Run(name, func(t *testing.T) {
_, err = dir.Open("/index.html/not-a-file")
_, err = fsys.Open("/index.html/not-a-file")
if err == nil {
t.Fatal("err == nil; want != nil")
}
@ -1270,7 +1279,7 @@ func TestFileServerNotDirError(t *testing.T) {
errors.Is(err, fs.ErrNotExist))
}
_, err = dir.Open("/index.html/not-a-dir/not-a-file")
_, err = fsys.Open("/index.html/not-a-dir/not-a-file")
if err == nil {
t.Fatal("err == nil; want != nil")
}
@ -1286,8 +1295,8 @@ func TestFileServerNotDirError(t *testing.T) {
t.Fatal("get abs path:", err)
}
test("RelativePath", Dir("testdata"))
test("AbsolutePath", Dir(absPath))
test("RelativePath", newfs("testdata"))
test("AbsolutePath", newfs(absPath))
}
func TestFileServerCleanPath(t *testing.T) {