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"
 | 
						"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)
 | 
				
			||||||
		}
 | 
							}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
				
			|||||||
		Reference in New Issue
	
	Block a user