Skip to content

Commit

Permalink
add fs.FS support (lpar#19)
Browse files Browse the repository at this point in the history
Signed-off-by: Adphi <[email protected]>
  • Loading branch information
Adphi committed Jun 29, 2022
1 parent 2d3caea commit 49cacce
Show file tree
Hide file tree
Showing 4 changed files with 129 additions and 58 deletions.
155 changes: 104 additions & 51 deletions fileserver_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,8 @@ package gzipped
import (
"bytes"
"compress/gzip"
"embed"
fs2 "io/fs"
"io/ioutil"
"net/http"
"net/http/httptest"
Expand Down Expand Up @@ -34,8 +36,8 @@ func TestPreference(t *testing.T) {
}
}

func testGet(t *testing.T, acceptGzip bool, urlPath string, expectedBody string) {
fs := FileServer(Dir("./testdata/"))
func testGet(t *testing.T, f FileSystem, acceptGzip bool, urlPath string, expectedBody string) {
fs := FileServer(f)
rr := httptest.NewRecorder()
req, _ := http.NewRequest("GET", urlPath, nil)
if acceptGzip {
Expand Down Expand Up @@ -88,62 +90,113 @@ func testGet(t *testing.T, acceptGzip bool, urlPath string, expectedBody string)
}
}

func TestOpenStat(t *testing.T) {
fh := &fileHandler{Dir(".")}
_, _, err := fh.openAndStat(".")
if err == nil {
t.Errorf("openAndStat directory succeeded, should have failed")
}
_, _, err = fh.openAndStat("updog")
if err == nil {
t.Errorf("openAndStat nonexistent file succeeded, should have failed")
}
}
//go:embed testdata
var testData embed.FS

func TestNoBrowse(t *testing.T) {
fs := FileServer(Dir("./testdata/"))
rr := httptest.NewRecorder()
req, _ := http.NewRequest("GET", "/", nil)
fs.ServeHTTP(rr, req)
if rr.Code != 404 {
t.Errorf("Directory browse succeeded")
}
type TestCase struct {
name string
test func(t *testing.T)
}

func TestLeadingSlash(t *testing.T) {
fs := FileServer(Dir("./testdata/"))
rr := httptest.NewRecorder()
req, _ := http.NewRequest("GET", "file.txt", nil)
fs.ServeHTTP(rr, req)
if rr.Code != 200 {
t.Errorf("Missing leading / on HTTP path caused error")
func TestFileServer(t *testing.T) {
tests := func(f FileSystem) []TestCase {
return []TestCase{
{
name: "OpenStat",
test: func(t *testing.T) {
fh := &fileHandler{f}
_, _, err := fh.openAndStat(".")
if err == nil {
t.Errorf("openAndStat directory succeeded, should have failed")
}
_, _, err = fh.openAndStat("updog")
if err == nil {
t.Errorf("openAndStat nonexistent file succeeded, should have failed")
}
},
},
{

name: "NoBrowse",
test: func(t *testing.T) {
fs := FileServer(f)
rr := httptest.NewRecorder()
req, _ := http.NewRequest("GET", "/", nil)
fs.ServeHTTP(rr, req)
if rr.Code != 404 {
t.Errorf("Directory browse succeeded")
}
},
},
{

name: "LeadingSlash",
test: func(t *testing.T) {
fs := FileServer(f)
rr := httptest.NewRecorder()
req, _ := http.NewRequest("GET", "file.txt", nil)
fs.ServeHTTP(rr, req)
if rr.Code != 200 {
t.Errorf("Missing leading / on HTTP path caused error")
}
},
},
{

name: "404",
test: func(t *testing.T) {
fs := FileServer(f)
rr := httptest.NewRecorder()
req, _ := http.NewRequest("GET", "/nonexistent.txt", nil)
fs.ServeHTTP(rr, req)
if rr.Code != 404 {
t.Errorf("Directory browse succeeded")
}
},
},
{

name: "Get",
test: func(t *testing.T) {
testGet(t, f, false, "/file.txt", "zyxwvutsrqponmlkjihgfedcba\n")
},
},
{

name: "GzipGet",
test: func(t *testing.T) {
testGet(t, f, true, "/file.txt", "abcdefghijklmnopqrstuvwxyz\n")
},
},
{

name: "GetIdentity",
test: func(t *testing.T) {
testGet(t, f, false, "/file2.txt", "1234567890987654321\n")
},
},
{

name: "GzipGetIdentity",
test: func(t *testing.T) {
testGet(t, f, true, "/file2.txt", "1234567890987654321\n")
},
},
}
}
}

func Test404(t *testing.T) {
fs := FileServer(Dir("./testdata/"))
rr := httptest.NewRecorder()
req, _ := http.NewRequest("GET", "/nonexistent.txt", nil)
fs.ServeHTTP(rr, req)
if rr.Code != 404 {
t.Errorf("Directory browse succeeded")
sub, err := fs2.Sub(testData, "testdata")
if err != nil {
t.Fatal(err)
}
}

func TestGet(t *testing.T) {
testGet(t, false, "/file.txt", "zyxwvutsrqponmlkjihgfedcba\n")
}

func TestGzipGet(t *testing.T) {
testGet(t, true, "/file.txt", "abcdefghijklmnopqrstuvwxyz\n")
}

func TestGetIdentity(t *testing.T) {
testGet(t, false, "/file2.txt", "1234567890987654321\n")
}

func TestGzipGetIdentity(t *testing.T) {
testGet(t, true, "/file2.txt", "1234567890987654321\n")
for name, fs := range map[string]FileSystem{"dir": Dir("./testdata/"), "fs": FS(sub)} {
t.Run(name, func(t *testing.T) {
for _, tt := range tests(fs) {
t.Run(tt.name, tt.test)
}
})
}
}

func TestConstHeaders(t *testing.T) {
Expand Down
23 changes: 23 additions & 0 deletions filesystem.go
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
package gzipped

import (
fs2 "io/fs"
"net/http"
"os"
"path"
Expand Down Expand Up @@ -36,3 +37,25 @@ func (d Dir) Exists(name string) bool {
func (d Dir) Open(name string) (http.File, error) {
return http.Dir(d).Open(name)
}

func FS(f fs2.FS) FileSystem {
return fs{fs: f}
}

type fs struct {
fs fs2.FS
}

// Exists tests whether a file with the specified name exists, resolved relative to the file system.
func (f fs) Exists(name string) bool {
if filepath.Separator != '/' && strings.ContainsRune(name, filepath.Separator) {
return false
}
_, err := fs2.Stat(f.fs, strings.TrimPrefix(filepath.FromSlash(path.Clean(name)), "/"))
return err == nil
}

// Open defers to http.FS's Open so that gzipped.fs implements http.FileSystem.
func (f fs) Open(name string) (http.File, error) {
return http.FS(f.fs).Open(strings.TrimPrefix(name, "/"))
}
6 changes: 2 additions & 4 deletions go.mod
Original file line number Diff line number Diff line change
@@ -1,7 +1,5 @@
module github.com/lpar/gzipped/v2

require (
github.com/kevinpollet/nego v0.0.0-20200324111829-b3061ca9dd9d
)
require github.com/kevinpollet/nego v0.0.0-20200324111829-b3061ca9dd9d

go 1.13
go 1.16
3 changes: 0 additions & 3 deletions go.sum
Original file line number Diff line number Diff line change
Expand Up @@ -2,14 +2,11 @@ github.com/davecgh/go-spew v1.1.0 h1:ZDRjVQ15GmhC3fiQ8ni8+OwkZQO4DARzQgrnXU1Liz8
github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
github.com/kevinpollet/nego v0.0.0-20200324111829-b3061ca9dd9d h1:BaIpmhcqpBnz4+NZjUjVGxKNA+/E7ovKsjmwqjXcGYc=
github.com/kevinpollet/nego v0.0.0-20200324111829-b3061ca9dd9d/go.mod h1:3FSWkzk9h42opyV0o357Fq6gsLF/A6MI/qOca9kKobY=
github.com/lpar/accept v0.1.0 h1:q4+k1TJuCfoe8cIBRLTfiMMoiaHKpn1rlD1yYE/wj7o=
github.com/lpar/accept v0.1.0/go.mod h1:/ZcJqAhzugu4J4EZ7hwixKslL0y07dMxQzIZZLpbZbk=
github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM=
github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4=
github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME=
github.com/stretchr/testify v1.5.1 h1:nOGnQDM7FYENwehXlg/kFVnos3rEvtKTjRvOWSzb6H4=
github.com/stretchr/testify v1.5.1/go.mod h1:5W2xD1RspED5o8YsWQXVCued0rvSQ+mT+I5cxcmMvtA=
gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405 h1:yhCVgyC4o1eVCa2tZl7eS0r+SDo693bJlVdllGtEeKM=
gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
gopkg.in/yaml.v2 v2.2.2 h1:ZCJp+EgiOT7lHqUV2J862kp8Qj64Jo6az82+3Td9dZw=
gopkg.in/yaml.v2 v2.2.2/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=

0 comments on commit 49cacce

Please sign in to comment.