Skip to content

Commit

Permalink
feat(fs): Export, test and document OnlyFilesFS (#3939)
Browse files Browse the repository at this point in the history
  • Loading branch information
joeig committed May 8, 2024
1 parent b1c1e7b commit 8791c96
Show file tree
Hide file tree
Showing 3 changed files with 100 additions and 23 deletions.
50 changes: 28 additions & 22 deletions fs.go
Expand Up @@ -9,37 +9,43 @@ import (
"os"
)

type onlyFilesFS struct {
fs http.FileSystem
// OnlyFilesFS implements an http.FileSystem without `Readdir` functionality.
type OnlyFilesFS struct {
FileSystem http.FileSystem
}

type neuteredReaddirFile struct {
// Open passes `Open` to the upstream implementation without `Readdir` functionality.
func (o OnlyFilesFS) Open(name string) (http.File, error) {
f, err := o.FileSystem.Open(name)

if err != nil {
return nil, err
}

return neutralizedReaddirFile{f}, nil
}

// neutralizedReaddirFile wraps http.File with a specific implementation of `Readdir`.
type neutralizedReaddirFile struct {
http.File
}

// 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.
// Readdir overrides the http.File default implementation and always returns nil.
func (n neutralizedReaddirFile) Readdir(_ int) ([]os.FileInfo, error) {
// this disables directory listing
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{fs}
}

// Open conforms to http.Filesystem.
func (fs onlyFilesFS) Open(name string) (http.File, error) {
f, err := fs.fs.Open(name)
if err != nil {
return nil, err
}
return neuteredReaddirFile{f}, nil
}

// Readdir overrides the http.File default implementation.
func (f neuteredReaddirFile) Readdir(_ int) ([]os.FileInfo, error) {
// this disables directory listing
return nil, nil
return &OnlyFilesFS{FileSystem: fs}
}
71 changes: 71 additions & 0 deletions fs_test.go
@@ -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)
}
2 changes: 1 addition & 1 deletion routergroup.go
Expand Up @@ -218,7 +218,7 @@ func (group *RouterGroup) createStaticHandler(relativePath string, fs http.FileS
fileServer := http.StripPrefix(absolutePath, http.FileServer(fs))

return func(c *Context) {
if _, noListing := fs.(*onlyFilesFS); noListing {
if _, noListing := fs.(*OnlyFilesFS); noListing {
c.Writer.WriteHeader(http.StatusNotFound)
}

Expand Down

0 comments on commit 8791c96

Please sign in to comment.