Bläddra i källkod

Fix lack of modtime in vessel zip files

spsobole 1 år sedan
förälder
incheckning
7528df9b03
5 ändrade filer med 67 tillägg och 33 borttagningar
  1. 22 17
      bundle.go
  2. 1 0
      http_file.go
  3. 12 3
      http_fs.go
  4. 16 13
      overlay.go
  5. 16 0
      vessel_test.go

+ 22 - 17
bundle.go

@@ -8,6 +8,7 @@ import (
 	"os"
 	"path/filepath"
 	"strings"
+	"time"
 )
 
 const (
@@ -18,11 +19,12 @@ const (
 
 // Bundle wraps the contents of a zip file for use
 type Bundle struct {
-	offset  int64
-	size    int64
-	version uint64
-	magic   uint64
-	file    *os.File
+	offset    int64
+	size      int64
+	version   uint64
+	magic     uint64
+	file      *os.File
+	cacheTime time.Time
 }
 
 // Close close the bundle
@@ -62,13 +64,19 @@ func (b *Bundle) ReadAt(p []byte, off int64) (n int, err error) {
 	return b.file.ReadAt(p, off)
 }
 
+// WithTimeOverride  override the time on files in the filesystem ( to subvert caches)
+// with this set when a FS is created all files/directories will report this date
+func (b *Bundle) WithTimeOverride(modTime time.Time) {
+	b.cacheTime = modTime
+}
+
 // FileSystem returns a handle to an bundle as a FileSystem that implements http.Filesystem
 func (b *Bundle) FileSystem() *FileSystem {
 	f := &FileSystem{
 		b: b,
 	}
 
-	f.initCaches()
+	f.initCaches(b.cacheTime)
 	return f
 }
 
@@ -229,11 +237,6 @@ func packInto(dst *os.File, src string, filter func(file string) bool) error {
 		}
 		if info.IsDir() {
 			return nil
-			//continue
-			//err = filepath.Walk(path, walker)
-			//if err != nil {
-			//	return err
-			//}
 		}
 		file, err := os.Open(path)
 		if err != nil {
@@ -241,19 +244,21 @@ func packInto(dst *os.File, src string, filter func(file string) bool) error {
 		}
 		defer file.Close()
 
-		// Ensure that `path` is not absolute; it should not start with "/".
-		// This snippet happens to work because I don't use
-		// absolute paths, but ensure your real-world code
-		// transforms path into a zip-root relative path.
-		f, err := w.Create(spath)
+		header, err := zip.FileInfoHeader(info)
 		if err != nil {
 			return err
 		}
-		_, err = io.Copy(f, file)
+		header.Name = spath
+
+		fileWriter, err := w.CreateHeader(header)
 		if err != nil {
 			return err
 		}
 
+		_, err = io.Copy(fileWriter, file)
+		if err != nil {
+			return err
+		}
 		return nil
 	}
 	err := filepath.Walk(src, walker)

+ 1 - 0
http_file.go

@@ -22,6 +22,7 @@ func newFile(z *zip.File) (*File, error) {
 	if err != nil {
 		return nil, err
 	}
+
 	return &File{
 		r:   r,
 		zip: z,

+ 12 - 3
http_fs.go

@@ -8,12 +8,14 @@ import (
 	"path"
 	"path/filepath"
 	"strings"
+	"time"
 )
 
 // FileSystem implements an http filesystem access to the bundle
 type FileSystem struct {
-	b     *Bundle
-	files map[string]os.FileInfo
+	cacheTime time.Time
+	b         *Bundle
+	files     map[string]os.FileInfo
 }
 
 // verify we implement the http.FileSystem interface
@@ -58,6 +60,7 @@ func (f *FileSystem) Open(name string) (http.File, error) {
 	}
 
 	if !z.FileInfo().IsDir() {
+		z.Modified = v.ModTime()
 		return newFile(z)
 	}
 
@@ -107,15 +110,21 @@ func (f *FileSystem) Dir(path string) ([]os.FileInfo, error) {
 	return ls, nil
 }
 
-func (f *FileSystem) initCaches() error {
+func (f *FileSystem) initCaches(cacheTime time.Time) error {
 	zr, err := zip.NewReader(f.b, f.b.Size())
 	if err != nil {
 		return os.ErrNotExist
 	}
+	f.cacheTime = cacheTime
 
 	files := make(map[string]os.FileInfo)
 	for idx := range zr.File {
 		z := zr.File[idx]
+
+		if !f.cacheTime.IsZero() {
+			z.Modified = f.cacheTime
+		}
+
 		files[z.Name] = z.FileInfo()
 	}
 

+ 16 - 13
overlay.go

@@ -1,8 +1,8 @@
 package vessel
 
 import (
-	"net/http"
 	"io/fs"
+	"net/http"
 	"os"
 )
 
@@ -27,8 +27,7 @@ func (f *OverlayFS) Append(fs http.FileSystem) {
 
 // Open implements http.FileSystem
 func (f *OverlayFS) Open(name string) (http.File, error) {
-	dir := &OverlayDir{
-	}
+	dir := &OverlayDir{}
 
 	for _, source := range f.search {
 		file, err := source.Open(name)
@@ -38,7 +37,7 @@ func (f *OverlayFS) Open(name string) (http.File, error) {
 				continue
 			}
 
-			if ! s.IsDir() {
+			if !s.IsDir() {
 				return file, nil
 			}
 
@@ -53,17 +52,17 @@ func (f *OverlayFS) Open(name string) (http.File, error) {
 	return nil, os.ErrNotExist
 }
 
-
 type OverlayDir struct {
 	files []http.File
 }
 
-func  (f *OverlayDir) Readdir(count int) ([]fs.FileInfo, error) {
+// Readdir implements http.FileSystem Readdir
+func (f *OverlayDir) Readdir(count int) ([]fs.FileInfo, error) {
 	// for read dir we need all of the content
 	content := make(map[string]fs.FileInfo)
 
 	for _, dir := range f.files {
-		list,err  := dir.Readdir(count)
+		list, err := dir.Readdir(count)
 		if err != nil {
 			continue
 		}
@@ -76,7 +75,7 @@ func  (f *OverlayDir) Readdir(count int) ([]fs.FileInfo, error) {
 		}
 	}
 
-	files := make([]fs.FileInfo,0, count)
+	files := make([]fs.FileInfo, 0, count)
 	for k := range content {
 		files = append(files, content[k])
 	}
@@ -88,18 +87,22 @@ func  (f *OverlayDir) Readdir(count int) ([]fs.FileInfo, error) {
 	return files, nil
 }
 
-func  (f *OverlayDir) Stat() (fs.FileInfo, error) {
+// Stat implements http.FileSystem Stat
+func (f *OverlayDir) Stat() (fs.FileInfo, error) {
 	return f.files[0].Stat()
 }
 
-func  (f *OverlayDir) Seek(offset int64, whence int) (int64, error) {
+// Seek implements http.FileSystem Seek
+func (f *OverlayDir) Seek(offset int64, whence int) (int64, error) {
 	return f.files[0].Seek(offset, whence)
 }
 
-func  (f *OverlayDir) 	Read(p []byte) (n int, err error) {
+// Read implements http.FileSystem Read
+func (f *OverlayDir) Read(p []byte) (n int, err error) {
 	return f.files[0].Read(p)
 }
 
-func  (f *OverlayDir) Close() error {
+// Close implements http.FileSystem Close
+func (f *OverlayDir) Close() error {
 	return f.files[0].Close()
-}
+}

+ 16 - 0
vessel_test.go

@@ -6,6 +6,7 @@ import (
 	"path/filepath"
 	"strings"
 	"testing"
+	"time"
 
 	"github.com/stretchr/testify/assert"
 )
@@ -58,4 +59,19 @@ func TestVessel(t *testing.T) {
 	stat, err = fs.Stat("/site/templates/list.html")
 	assert.Nil(t, err)
 	assert.Equal(t, false, stat.IsDir())
+
+	lstat, err := os.Stat("example/" + "/site/templates/list.html")
+	assert.NoError(t, err)
+	assert.Equal(t, lstat.ModTime().UTC(), stat.ModTime().UTC())
+
+	now := time.Now()
+	b.WithTimeOverride(now)
+	fs = b.FileSystem()
+	assert.NotNil(t, fs)
+
+	stat, err = fs.Stat("/site/templates/list.html")
+	assert.Nil(t, err)
+
+	assert.NoError(t, err)
+	assert.Equal(t, now.UTC(), stat.ModTime().UTC())
 }