diff options
Diffstat (limited to 'internal/vfs/zip/archive.go')
-rw-r--r-- | internal/vfs/zip/archive.go | 42 |
1 files changed, 31 insertions, 11 deletions
diff --git a/internal/vfs/zip/archive.go b/internal/vfs/zip/archive.go index df175764..1137f004 100644 --- a/internal/vfs/zip/archive.go +++ b/internal/vfs/zip/archive.go @@ -32,13 +32,21 @@ var ( errNotFile = errors.New("not a file") ) +type archiveStatus int + +const ( + archiveOpening archiveStatus = iota + archiveOpenError + archiveOpened + archiveCorrupted +) + // zipArchive implements the vfs.Root interface. // It represents a zip archive saving all its files in memory. // It holds an httprange.Resource that can be read with httprange.RangedReader in chunks. type zipArchive struct { fs *zipVFS - path string once sync.Once done chan struct{} openTimeout time.Duration @@ -54,10 +62,9 @@ type zipArchive struct { directories map[string]*zip.FileHeader } -func newArchive(fs *zipVFS, path string, openTimeout time.Duration) *zipArchive { +func newArchive(fs *zipVFS, openTimeout time.Duration) *zipArchive { return &zipArchive{ fs: fs, - path: path, done: make(chan struct{}), files: make(map[string]*zip.File), directories: make(map[string]*zip.FileHeader), @@ -66,9 +73,14 @@ func newArchive(fs *zipVFS, path string, openTimeout time.Duration) *zipArchive } } -func (a *zipArchive) openArchive(parentCtx context.Context) (err error) { +func (a *zipArchive) openArchive(parentCtx context.Context, url string) (err error) { + // always try to update URL on resource + if a.resource != nil { + a.resource.SetURL(url) + } + // return early if openArchive was done already in a concurrent request - if ok, err := a.openStatus(); ok { + if status, err := a.openStatus(); status != archiveOpening { return err } @@ -78,7 +90,7 @@ func (a *zipArchive) openArchive(parentCtx context.Context) (err error) { a.once.Do(func() { // read archive once in its own routine with its own timeout // if parentCtx is canceled, readArchive will continue regardless and will be cached in memory - go a.readArchive() + go a.readArchive(url) }) // wait for readArchive to be done or return if the parent context is canceled @@ -100,14 +112,14 @@ func (a *zipArchive) openArchive(parentCtx context.Context) (err error) { // readArchive creates an httprange.Resource that can read the archive's contents and stores a slice of *zip.Files // that can be accessed later when calling any of th vfs.VFS operations -func (a *zipArchive) readArchive() { +func (a *zipArchive) readArchive(url string) { defer close(a.done) // readArchive with a timeout separate from openArchive's ctx, cancel := context.WithTimeout(context.Background(), a.openTimeout) defer cancel() - a.resource, a.err = httprange.NewResource(ctx, a.path) + a.resource, a.err = httprange.NewResource(ctx, url) if a.err != nil { metrics.ZipOpened.WithLabelValues("error").Inc() return @@ -281,12 +293,20 @@ func (a *zipArchive) onEvicted() { metrics.ZipArchiveEntriesCached.Sub(float64(len(a.files))) } -func (a *zipArchive) openStatus() (bool, error) { +func (a *zipArchive) openStatus() (archiveStatus, error) { select { case <-a.done: - return true, a.err + if a.err != nil { + return archiveOpenError, a.err + } + + if a.resource != nil && a.resource.Err() != nil { + return archiveCorrupted, a.resource.Err() + } + + return archiveOpened, nil default: - return false, nil + return archiveOpening, nil } } |