feat(fs): Export, test and document OnlyFilesFS (#3939)
This commit is contained in:
parent
b1c1e7b572
commit
8791c96960
52
fs.go
52
fs.go
@ -9,37 +9,43 @@ import (
|
|||||||
"os"
|
"os"
|
||||||
)
|
)
|
||||||
|
|
||||||
type onlyFilesFS struct {
|
// OnlyFilesFS implements an http.FileSystem without `Readdir` functionality.
|
||||||
fs http.FileSystem
|
type OnlyFilesFS struct {
|
||||||
|
FileSystem http.FileSystem
|
||||||
}
|
}
|
||||||
|
|
||||||
type neuteredReaddirFile struct {
|
// Open passes `Open` to the upstream implementation without `Readdir` functionality.
|
||||||
http.File
|
func (o OnlyFilesFS) Open(name string) (http.File, error) {
|
||||||
}
|
f, err := o.FileSystem.Open(name)
|
||||||
|
|
||||||
// Dir returns a http.FileSystem that can be used by http.FileServer(). It is used internally
|
|
||||||
// in router.Static().
|
|
||||||
// if listDirectory == true, then it works the same as http.Dir() otherwise it returns
|
|
||||||
// a filesystem that prevents http.FileServer() to list the directory files.
|
|
||||||
func Dir(root string, listDirectory bool) http.FileSystem {
|
|
||||||
fs := http.Dir(root)
|
|
||||||
if listDirectory {
|
|
||||||
return fs
|
|
||||||
}
|
|
||||||
return &onlyFilesFS{fs}
|
|
||||||
}
|
|
||||||
|
|
||||||
// Open conforms to http.Filesystem.
|
|
||||||
func (fs onlyFilesFS) Open(name string) (http.File, error) {
|
|
||||||
f, err := fs.fs.Open(name)
|
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
return neuteredReaddirFile{f}, nil
|
|
||||||
|
return neutralizedReaddirFile{f}, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
// Readdir overrides the http.File default implementation.
|
// neutralizedReaddirFile wraps http.File with a specific implementation of `Readdir`.
|
||||||
func (f neuteredReaddirFile) Readdir(_ int) ([]os.FileInfo, error) {
|
type neutralizedReaddirFile struct {
|
||||||
|
http.File
|
||||||
|
}
|
||||||
|
|
||||||
|
// Readdir overrides the http.File default implementation and always returns nil.
|
||||||
|
func (n neutralizedReaddirFile) Readdir(_ int) ([]os.FileInfo, error) {
|
||||||
// this disables directory listing
|
// this disables directory listing
|
||||||
return nil, nil
|
return nil, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Dir returns an http.FileSystem that can be used by http.FileServer().
|
||||||
|
// It is used internally in router.Static().
|
||||||
|
// if listDirectory == true, then it works the same as http.Dir(),
|
||||||
|
// otherwise it returns a filesystem that prevents http.FileServer() to list the directory files.
|
||||||
|
func Dir(root string, listDirectory bool) http.FileSystem {
|
||||||
|
fs := http.Dir(root)
|
||||||
|
|
||||||
|
if listDirectory {
|
||||||
|
return fs
|
||||||
|
}
|
||||||
|
|
||||||
|
return &OnlyFilesFS{FileSystem: fs}
|
||||||
|
}
|
||||||
|
71
fs_test.go
Normal file
71
fs_test.go
Normal file
@ -0,0 +1,71 @@
|
|||||||
|
package gin
|
||||||
|
|
||||||
|
import (
|
||||||
|
"errors"
|
||||||
|
"net/http"
|
||||||
|
"os"
|
||||||
|
"testing"
|
||||||
|
|
||||||
|
"github.com/stretchr/testify/assert"
|
||||||
|
)
|
||||||
|
|
||||||
|
type mockFileSystem struct {
|
||||||
|
open func(name string) (http.File, error)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (m *mockFileSystem) Open(name string) (http.File, error) {
|
||||||
|
return m.open(name)
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestOnlyFilesFS_Open(t *testing.T) {
|
||||||
|
var testFile *os.File
|
||||||
|
mockFS := &mockFileSystem{
|
||||||
|
open: func(name string) (http.File, error) {
|
||||||
|
return testFile, nil
|
||||||
|
},
|
||||||
|
}
|
||||||
|
fs := &OnlyFilesFS{FileSystem: mockFS}
|
||||||
|
|
||||||
|
file, err := fs.Open("foo")
|
||||||
|
|
||||||
|
assert.NoError(t, err)
|
||||||
|
assert.Equal(t, testFile, file.(neutralizedReaddirFile).File)
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestOnlyFilesFS_Open_err(t *testing.T) {
|
||||||
|
testError := errors.New("mock")
|
||||||
|
mockFS := &mockFileSystem{
|
||||||
|
open: func(_ string) (http.File, error) {
|
||||||
|
return nil, testError
|
||||||
|
},
|
||||||
|
}
|
||||||
|
fs := &OnlyFilesFS{FileSystem: mockFS}
|
||||||
|
|
||||||
|
file, err := fs.Open("foo")
|
||||||
|
|
||||||
|
assert.ErrorIs(t, err, testError)
|
||||||
|
assert.Nil(t, file)
|
||||||
|
}
|
||||||
|
|
||||||
|
func Test_neuteredReaddirFile_Readdir(t *testing.T) {
|
||||||
|
n := neutralizedReaddirFile{}
|
||||||
|
|
||||||
|
res, err := n.Readdir(0)
|
||||||
|
|
||||||
|
assert.NoError(t, err)
|
||||||
|
assert.Nil(t, res)
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestDir_listDirectory(t *testing.T) {
|
||||||
|
testRoot := "foo"
|
||||||
|
fs := Dir(testRoot, true)
|
||||||
|
|
||||||
|
assert.Equal(t, http.Dir(testRoot), fs)
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestDir(t *testing.T) {
|
||||||
|
testRoot := "foo"
|
||||||
|
fs := Dir(testRoot, false)
|
||||||
|
|
||||||
|
assert.Equal(t, &OnlyFilesFS{FileSystem: http.Dir(testRoot)}, fs)
|
||||||
|
}
|
@ -218,7 +218,7 @@ func (group *RouterGroup) createStaticHandler(relativePath string, fs http.FileS
|
|||||||
fileServer := http.StripPrefix(absolutePath, http.FileServer(fs))
|
fileServer := http.StripPrefix(absolutePath, http.FileServer(fs))
|
||||||
|
|
||||||
return func(c *Context) {
|
return func(c *Context) {
|
||||||
if _, noListing := fs.(*onlyFilesFS); noListing {
|
if _, noListing := fs.(*OnlyFilesFS); noListing {
|
||||||
c.Writer.WriteHeader(http.StatusNotFound)
|
c.Writer.WriteHeader(http.StatusNotFound)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
Loading…
Reference in New Issue
Block a user