feat(fs): Export, test and document OnlyFilesFS (#3939)
This commit is contained in:
		
				
					committed by
					
						
						GitHub
					
				
			
			
				
	
			
			
			
						parent
						
							b1c1e7b572
						
					
				
				
					commit
					8791c96960
				
			
							
								
								
									
										52
									
								
								fs.go
									
									
									
									
									
								
							
							
						
						
									
										52
									
								
								fs.go
									
									
									
									
									
								
							@ -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 {
 | 
			
		||||
	http.File
 | 
			
		||||
}
 | 
			
		||||
// 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)
 | 
			
		||||
 | 
			
		||||
// 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 {
 | 
			
		||||
		return nil, err
 | 
			
		||||
	}
 | 
			
		||||
	return neuteredReaddirFile{f}, nil
 | 
			
		||||
 | 
			
		||||
	return neutralizedReaddirFile{f}, nil
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// Readdir overrides the http.File default implementation.
 | 
			
		||||
func (f neuteredReaddirFile) Readdir(_ int) ([]os.FileInfo, error) {
 | 
			
		||||
// neutralizedReaddirFile wraps http.File with a specific implementation of `Readdir`.
 | 
			
		||||
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
 | 
			
		||||
	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))
 | 
			
		||||
 | 
			
		||||
	return func(c *Context) {
 | 
			
		||||
		if _, noListing := fs.(*onlyFilesFS); noListing {
 | 
			
		||||
		if _, noListing := fs.(*OnlyFilesFS); noListing {
 | 
			
		||||
			c.Writer.WriteHeader(http.StatusNotFound)
 | 
			
		||||
		}
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
		Reference in New Issue
	
	Block a user