From defc0948f82fbe8f033329724b92fe04ff3bfed0 Mon Sep 17 00:00:00 2001 From: Christian Stewart Date: Thu, 14 Jul 2022 15:12:06 -0700 Subject: [PATCH 1/3] .github: update go 1.18 Signed-off-by: Christian Stewart --- .github/workflows/test.yml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/.github/workflows/test.yml b/.github/workflows/test.yml index 4e9980e..ee09bbc 100644 --- a/.github/workflows/test.yml +++ b/.github/workflows/test.yml @@ -4,7 +4,7 @@ jobs: test: strategy: matrix: - go-version: [1.14.x, 1.15.x, 1.16.x] + go-version: [1.18.x] platform: [ubuntu-latest, macos-latest, windows-latest] runs-on: ${{ matrix.platform }} steps: @@ -15,4 +15,4 @@ jobs: - name: Checkout code uses: actions/checkout@v2 - name: Test - run: go test ./... \ No newline at end of file + run: go test ./... From 88733e9a675aa382f554eb08422f547a574c50a9 Mon Sep 17 00:00:00 2001 From: Christian Stewart Date: Thu, 14 Jul 2022 15:12:38 -0700 Subject: [PATCH 2/3] chore: update go.mod to go 1.18 Signed-off-by: Christian Stewart --- go.mod | 13 ++++++++----- go.sum | 15 ++++++--------- 2 files changed, 14 insertions(+), 14 deletions(-) diff --git a/go.mod b/go.mod index 78ce0af..de69d0a 100644 --- a/go.mod +++ b/go.mod @@ -1,10 +1,13 @@ module github.com/go-git/go-billy/v5 +go 1.18 + require ( - github.com/kr/text v0.2.0 // indirect - github.com/niemeyer/pretty v0.0.0-20200227124842-a10e7caefd8e // indirect - golang.org/x/sys v0.0.0-20200302150141-5c8b2ff67527 - gopkg.in/check.v1 v1.0.0-20200227125254-8fa46927fb4f + golang.org/x/sys v0.0.0-20220712014510-0a85c31ab51e + gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c ) -go 1.13 +require ( + github.com/kr/pretty v0.2.1 // indirect + github.com/kr/text v0.2.0 // indirect +) diff --git a/go.sum b/go.sum index cdc052b..8d66da1 100644 --- a/go.sum +++ b/go.sum @@ -1,14 +1,11 @@ github.com/creack/pty v1.1.9/go.mod h1:oKZEueFk5CKHvIhNR5MUki03XCEU+Q6VDXinZuGJ33E= -github.com/go-git/go-billy v1.0.0 h1:bXR6Zu3opPSg0R4dDxqaLglY4rxw7ja7wS16qSpOKL4= -github.com/go-git/go-billy v3.1.0+incompatible h1:dwrJ8G2Jt1srYgIJs+lRjA36qBY68O2Lg5idKG8ef5M= +github.com/kr/pretty v0.2.1 h1:Fmg33tUaq4/8ym9TJN1x7sLJnHVwhP33CNkpYV/7rwI= +github.com/kr/pretty v0.2.1/go.mod h1:ipq/a2n7PKx3OHsz4KJII5eveXtPO4qwEXGdVfWzfnI= github.com/kr/pty v1.1.1/go.mod h1:pFQYn66WHrOpPYNljwOMqo10TkYh1fy3cYio2l3bCsQ= -github.com/kr/text v0.1.0 h1:45sCR5RtlFHMR4UwH9sdQ5TC8v0qDQCHnXt+kaKSTVE= github.com/kr/text v0.1.0/go.mod h1:4Jbv+DJW3UT/LiOwJeYQe1efqtUx/iVham/4vfdArNI= github.com/kr/text v0.2.0 h1:5Nx0Ya0ZqY2ygV366QzturHI13Jq95ApcVaJBhpS+AY= github.com/kr/text v0.2.0/go.mod h1:eLer722TekiGuMkidMxC/pM04lWEeraHUUmBw8l2grE= -github.com/niemeyer/pretty v0.0.0-20200227124842-a10e7caefd8e h1:fD57ERR4JtEqsWbfPhv4DMiApHyliiK5xCTNVSPiaAs= -github.com/niemeyer/pretty v0.0.0-20200227124842-a10e7caefd8e/go.mod h1:zD1mROLANZcx1PVRCS0qkT7pwLkGfwJo4zjcN/Tysno= -golang.org/x/sys v0.0.0-20200302150141-5c8b2ff67527 h1:uYVVQ9WP/Ds2ROhcaGPeIdVq0RIXVLwsHlnvJ+cT1So= -golang.org/x/sys v0.0.0-20200302150141-5c8b2ff67527/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -gopkg.in/check.v1 v1.0.0-20200227125254-8fa46927fb4f h1:BLraFXnmrev5lT+xlilqcH8XK9/i0At2xKjWk4p6zsU= -gopkg.in/check.v1 v1.0.0-20200227125254-8fa46927fb4f/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= +golang.org/x/sys v0.0.0-20220712014510-0a85c31ab51e h1:NHvCuwuS43lGnYhten69ZWqi2QOj/CiDNcKbVqwVoew= +golang.org/x/sys v0.0.0-20220712014510-0a85c31ab51e/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c h1:Hei/4ADfdWqJk1ZMxUNpqntNwaWcugrBjAiHlqqRiVk= +gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c/go.mod h1:JHkPIbrfpd72SG/EVd6muEfDQjcINNoR0C8j2r3qZ4Q= From e7bcd57d30b6a3c02ba305c2cec2f8d6a250eb1a Mon Sep 17 00:00:00 2001 From: Christian Stewart Date: Thu, 14 Jul 2022 15:01:29 -0700 Subject: [PATCH 3/3] httpfs: implement http.FileSystem interfaces Implements the http.FileSystem interfaces. This allows using http.FileServer with a BillyFS. Signed-off-by: Christian Stewart --- httpfs/dir.go | 52 +++++++++++++++++++++++++++++++++++ httpfs/file.go | 40 +++++++++++++++++++++++++++ httpfs/filesystem.go | 58 +++++++++++++++++++++++++++++++++++++++ httpfs/filesystem_test.go | 50 +++++++++++++++++++++++++++++++++ 4 files changed, 200 insertions(+) create mode 100644 httpfs/dir.go create mode 100644 httpfs/file.go create mode 100644 httpfs/filesystem.go create mode 100644 httpfs/filesystem_test.go diff --git a/httpfs/dir.go b/httpfs/dir.go new file mode 100644 index 0000000..842704d --- /dev/null +++ b/httpfs/dir.go @@ -0,0 +1,52 @@ +package httpfs + +import ( + "errors" + "io/fs" + "net/http" +) + +// Dir implements the HTTP directory. +type Dir struct { + // fs is the base filesysetm + fs BillyFs + // path is the path to this dir + path string +} + +// NewDir constructs the Dir from a Billy Dir. +func NewDir(fs BillyFs, path string) *Dir { + return &Dir{fs: fs, path: path} +} + +func (f *Dir) Stat() (fs.FileInfo, error) { + return f.fs.Stat(f.path) +} + +// Readdir reads the directory contents. +func (f *Dir) Readdir(count int) ([]fs.FileInfo, error) { + ents, err := f.fs.ReadDir(f.path) + if err != nil { + return nil, err + } + if count > 0 && count > len(ents) { + ents = ents[:count] + } + return ents, err +} + +func (f *Dir) Read(p []byte) (n int, err error) { + return 0, errors.New("not a file") +} + +func (f *Dir) Seek(offset int64, whence int) (int64, error) { + return 0, errors.New("not a file") +} + +func (f *Dir) Close() error { + // no-op. + return nil +} + +// _ is a type assertion +var _ http.File = ((*Dir)(nil)) diff --git a/httpfs/file.go b/httpfs/file.go new file mode 100644 index 0000000..c85f381 --- /dev/null +++ b/httpfs/file.go @@ -0,0 +1,40 @@ +package httpfs + +import ( + "errors" + "io/fs" + "net/http" + + "github.com/go-git/go-billy/v5" +) + +// File implements the HTTP file. +type File struct { + // File is the billy file + billy.File + // path is the path to File + path string + // fs is the filesystem + fs BillyFs +} + +// NewFile constructs the File from a Billy File. +func NewFile(fs BillyFs, path string) (*File, error) { + f, err := fs.Open(path) + if err != nil { + return nil, err + } + return &File{File: f, path: path, fs: fs}, nil +} + +func (f *File) Readdir(count int) ([]fs.FileInfo, error) { + // ENOTDIR + return nil, errors.New("not a directory") +} + +func (f *File) Stat() (fs.FileInfo, error) { + return f.fs.Stat(f.path) +} + +// _ is a type assertion +var _ http.File = ((*File)(nil)) diff --git a/httpfs/filesystem.go b/httpfs/filesystem.go new file mode 100644 index 0000000..4a48b69 --- /dev/null +++ b/httpfs/filesystem.go @@ -0,0 +1,58 @@ +package httpfs + +import ( + "net/http" + "path" + "strings" + + "github.com/go-git/go-billy/v5" +) + +// BillyFs is the set of required billy filesystem interfaces. +type BillyFs interface { + billy.Basic + billy.Dir +} + +// FileSystem implements the HTTP filesystem. +type FileSystem struct { + // fs is the billy filesystem + fs BillyFs + // prefix is the filesystem prefix for HTTP + prefix string +} + +// NewFileSystem constructs the FileSystem from a Billy FileSystem. +// +// Prefix is a path prefix to prepend to file paths for HTTP. +// The prefix is trimmed from the paths when opening files. +func NewFileSystem(fs BillyFs, prefix string) *FileSystem { + if len(prefix) != 0 { + prefix = path.Clean(prefix) + } + return &FileSystem{fs: fs, prefix: prefix} +} + +// Open opens the file at the given path. +func (f *FileSystem) Open(name string) (http.File, error) { + name = path.Clean(name) + if len(f.prefix) != 0 { + name = strings.TrimPrefix(name, f.prefix) + name = path.Clean(name) + } + if strings.HasPrefix(name, "/") { + name = name[1:] + } + + fi, err := f.fs.Stat(name) + if err != nil { + return nil, err + } + if fi.IsDir() { + return NewDir(f.fs, name), nil + } + return NewFile(f.fs, name) +} + +// _ is a type assertion +var _ http.FileSystem = ((*FileSystem)(nil)) diff --git a/httpfs/filesystem_test.go b/httpfs/filesystem_test.go new file mode 100644 index 0000000..487fd79 --- /dev/null +++ b/httpfs/filesystem_test.go @@ -0,0 +1,50 @@ +package httpfs + +import ( + "bytes" + "io/ioutil" + "net/http" + "net/http/httptest" + "testing" + + "github.com/go-git/go-billy/v5/memfs" + "github.com/go-git/go-billy/v5/util" +) + +// TestFileSystem tests the HTTP filesystem. +func TestFileSystem(t *testing.T) { + mfs := memfs.New() + + err := mfs.MkdirAll("./stuff", 0755) + if err != nil { + t.Fatal(err.Error()) + } + + data := []byte("hello world!\n") + err = util.WriteFile(mfs, "./stuff/test.txt", data, 0755) + if err != nil { + t.Fatal(err.Error()) + } + + var hfs http.FileSystem = NewFileSystem(mfs, "/test") + + mux := http.NewServeMux() + mux.Handle("/", http.FileServer(hfs)) + + req := httptest.NewRequest("GET", "/test/stuff/test.txt", nil) + rw := httptest.NewRecorder() + mux.ServeHTTP(rw, req) + + res := rw.Result() + if res.StatusCode != 200 { + t.Fatalf("status code: %d", res.StatusCode) + } + + readData, err := ioutil.ReadAll(res.Body) + if err != nil { + t.Fatal(err.Error()) + } + if !bytes.Equal(readData, data) { + t.Fail() + } +}