Skip to content

Commit

Permalink
Improve filesystem support.
Browse files Browse the repository at this point in the history
  • Loading branch information
aldas committed Jan 15, 2022
1 parent c4d3df7 commit 34cacc2
Show file tree
Hide file tree
Showing 6 changed files with 68 additions and 32 deletions.
7 changes: 6 additions & 1 deletion context_fs_go1.16.go
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,11 @@ func (c *context) File(file string) error {
return fsFile(c, file, c.echo.Filesystem)
}

// FileFS serves file from given file system.
//
// When dealing with `embed.FS` use `fs := echo.MustSubFS(fs, "rootDirectory") to create sub fs which uses necessary
// prefix for directory path. This is necessary as `//go:embed assets/images` embeds files with paths
// including `assets/images` as their prefix.
func (c *context) FileFS(file string, filesystem fs.FS) error {
return fsFile(c, file, filesystem)
}
Expand All @@ -28,7 +33,7 @@ func fsFile(c Context, file string, filesystem fs.FS) error {

fi, _ := f.Stat()
if fi.IsDir() {
file = filepath.ToSlash(filepath.Join(file, indexPage))
file = filepath.Join(file, indexPage)
f, err = filesystem.Open(file)
if err != nil {
return ErrNotFound
Expand Down
18 changes: 9 additions & 9 deletions context_fs_go1.16_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@
package echo

import (
testify "github.com/stretchr/testify/assert"
"github.com/stretchr/testify/assert"
"io/fs"
"net/http"
"net/http/httptest"
Expand Down Expand Up @@ -62,18 +62,18 @@ func TestContext_File(t *testing.T) {

err := handler(c)

testify.Equal(t, tc.expectStatus, rec.Code)
assert.Equal(t, tc.expectStatus, rec.Code)
if tc.expectError != "" {
testify.EqualError(t, err, tc.expectError)
assert.EqualError(t, err, tc.expectError)
} else {
testify.NoError(t, err)
assert.NoError(t, err)
}

body := rec.Body.Bytes()
if len(body) > len(tc.expectStartsWith) {
body = body[:len(tc.expectStartsWith)]
}
testify.Equal(t, tc.expectStartsWith, body)
assert.Equal(t, tc.expectStartsWith, body)
})
}
}
Expand Down Expand Up @@ -118,18 +118,18 @@ func TestContext_FileFS(t *testing.T) {

err := handler(c)

testify.Equal(t, tc.expectStatus, rec.Code)
assert.Equal(t, tc.expectStatus, rec.Code)
if tc.expectError != "" {
testify.EqualError(t, err, tc.expectError)
assert.EqualError(t, err, tc.expectError)
} else {
testify.NoError(t, err)
assert.NoError(t, err)
}

body := rec.Body.Bytes()
if len(body) > len(tc.expectStartsWith) {
body = body[:len(tc.expectStartsWith)]
}
testify.Equal(t, tc.expectStartsWith, body)
assert.Equal(t, tc.expectStartsWith, body)
})
}
}
34 changes: 26 additions & 8 deletions echo_fs_go1.16.go
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,10 @@ import (
type filesystem struct {
// Filesystem is file system used by Static and File handlers to access files.
// Defaults to os.DirFS(".")
//
// When dealing with `embed.FS` use `fs := echo.MustSubFS(fs, "rootDirectory") to create sub fs which uses necessary
// prefix for directory path. This is necessary as `//go:embed assets/images` embeds files with paths
// including `assets/images` as their prefix.
Filesystem fs.FS
}

Expand All @@ -26,12 +30,8 @@ func createFilesystem() filesystem {
}

// Static registers a new route with path prefix to serve static files from the provided root directory.
func (e *Echo) Static(pathPrefix, root string) *Route {
subFs, err := subFS(e.Filesystem, root)
if err != nil {
// happens when `root` contains invalid path according to `fs.ValidPath` rules and we are unable to create FS
panic(fmt.Errorf("invalid root given to echo.Static, err %w", err))
}
func (e *Echo) Static(pathPrefix, fsRoot string) *Route {
subFs := MustSubFS(e.Filesystem, fsRoot)
return e.Add(
http.MethodGet,
pathPrefix+"*",
Expand All @@ -40,11 +40,15 @@ func (e *Echo) Static(pathPrefix, root string) *Route {
}

// StaticFS registers a new route with path prefix to serve static files from the provided file system.
func (e *Echo) StaticFS(pathPrefix string, fileSystem fs.FS) *Route {
//
// When dealing with `embed.FS` use `fs := echo.MustSubFS(fs, "rootDirectory") to create sub fs which uses necessary
// prefix for directory path. This is necessary as `//go:embed assets/images` embeds files with paths
// including `assets/images` as their prefix.
func (e *Echo) StaticFS(pathPrefix string, filesystem fs.FS) *Route {
return e.Add(
http.MethodGet,
pathPrefix+"*",
StaticDirectoryHandler(fileSystem, false),
StaticDirectoryHandler(filesystem, false),
)
}

Expand Down Expand Up @@ -125,3 +129,17 @@ func subFS(currentFs fs.FS, root string) (fs.FS, error) {
}
return fs.Sub(currentFs, root)
}

// MustSubFS creates sub FS from current filesystem or panic on failure.
// Panic happens when `fsRoot` contains invalid path according to `fs.ValidPath` rules.
//
// MustSubFS is helpful when dealing with `embed.FS` because for example `//go:embed assets/images` embeds files with
// paths including `assets/images` as their prefix. In that case use `fs := echo.MustSubFS(fs, "rootDirectory") to
// create sub fs which uses necessary prefix for directory path.
func MustSubFS(currentFs fs.FS, fsRoot string) fs.FS {
subFs, err := subFS(currentFs, fsRoot)
if err != nil {
panic(fmt.Errorf("can not create sub FS, invalid root given, err: %w", err))
}
return subFs
}
20 changes: 17 additions & 3 deletions echo_fs_go1.16_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,7 @@ func TestEcho_StaticFS(t *testing.T) {
name string
givenPrefix string
givenFs fs.FS
givenFsRoot string
whenURL string
expectStatus int
expectHeaderLocation string
Expand All @@ -31,6 +32,14 @@ func TestEcho_StaticFS(t *testing.T) {
expectStatus: http.StatusOK,
expectBodyStartsWith: string([]byte{0x89, 0x50, 0x4e, 0x47}),
},
{
name: "ok, from sub fs",
givenPrefix: "/images",
givenFs: MustSubFS(os.DirFS("./_fixture/"), "images"),
whenURL: "/images/walle.png",
expectStatus: http.StatusOK,
expectBodyStartsWith: string([]byte{0x89, 0x50, 0x4e, 0x47}),
},
{
name: "No file",
givenPrefix: "/images",
Expand Down Expand Up @@ -135,7 +144,12 @@ func TestEcho_StaticFS(t *testing.T) {
for _, tc := range testCases {
t.Run(tc.name, func(t *testing.T) {
e := New()
e.StaticFS(tc.givenPrefix, tc.givenFs)

tmpFs := tc.givenFs
if tc.givenFsRoot != "" {
tmpFs = MustSubFS(tmpFs, tc.givenFsRoot)
}
e.StaticFS(tc.givenPrefix, tmpFs)

req := httptest.NewRequest(http.MethodGet, tc.whenURL, nil)
rec := httptest.NewRecorder()
Expand Down Expand Up @@ -229,12 +243,12 @@ func TestEcho_StaticPanic(t *testing.T) {
{
name: "panics for ../",
givenRoot: "../assets",
expectError: "invalid root given to echo.Static, err sub ../assets: invalid name",
expectError: "can not create sub FS, invalid root given, err: sub ../assets: invalid name",
},
{
name: "panics for /",
givenRoot: "/assets",
expectError: "invalid root given to echo.Static, err sub /assets: invalid name",
expectError: "can not create sub FS, invalid root given, err: sub /assets: invalid name",
},
}

Expand Down
17 changes: 8 additions & 9 deletions group_fs_go1.16.go
Original file line number Diff line number Diff line change
Expand Up @@ -4,27 +4,26 @@
package echo

import (
"fmt"
"io/fs"
"net/http"
)

// Static implements `Echo#Static()` for sub-routes within the Group.
func (g *Group) Static(pathPrefix, root string) {
subFs, err := subFS(g.echo.Filesystem, root)
if err != nil {
// happens when `root` contains invalid path according to `fs.ValidPath` rules and we are unable to create FS
panic(fmt.Errorf("invalid root given to group.Static, err %w", err))
}
func (g *Group) Static(pathPrefix, fsRoot string) {
subFs := MustSubFS(g.echo.Filesystem, fsRoot)
g.StaticFS(pathPrefix, subFs)
}

// StaticFS implements `Echo#StaticFS()` for sub-routes within the Group.
func (g *Group) StaticFS(pathPrefix string, fileSystem fs.FS) {
//
// When dealing with `embed.FS` use `fs := echo.MustSubFS(fs, "rootDirectory") to create sub fs which uses necessary
// prefix for directory path. This is necessary as `//go:embed assets/images` embeds files with paths
// including `assets/images` as their prefix.
func (g *Group) StaticFS(pathPrefix string, filesystem fs.FS) {
g.Add(
http.MethodGet,
pathPrefix+"*",
StaticDirectoryHandler(fileSystem, false),
StaticDirectoryHandler(filesystem, false),
)
}

Expand Down
4 changes: 2 additions & 2 deletions group_fs_go1.16_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -82,12 +82,12 @@ func TestGroup_StaticPanic(t *testing.T) {
{
name: "panics for ../",
givenRoot: "../images",
expectError: "invalid root given to group.Static, err sub ../images: invalid name",
expectError: "can not create sub FS, invalid root given, err: sub ../images: invalid name",
},
{
name: "panics for /",
givenRoot: "/images",
expectError: "invalid root given to group.Static, err sub /images: invalid name",
expectError: "can not create sub FS, invalid root given, err: sub /images: invalid name",
},
}

Expand Down

0 comments on commit 34cacc2

Please sign in to comment.