Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

embed: modification time not set for embedded files #44854

Closed
eudore opened this issue Mar 8, 2021 · 2 comments
Closed

embed: modification time not set for embedded files #44854

eudore opened this issue Mar 8, 2021 · 2 comments

Comments

@eudore
Copy link

eudore commented Mar 8, 2021

What version of Go are you using (go version)?

$ go version
go version go1.16 linux/amd64

Does this issue reproduce with the latest release?

Yes

What operating system and processor architecture are you using (go env)?

go env Output
$ go env GOARCH GOOS
amd64
linux

What did you do?

package main

import (
	"embed"
	"fmt"
)

//go:embed static
var f embed.FS

func main() {
	f, err := f.Open("static/server.html")
	if err != nil {
		fmt.Println(err)
		return
	}
	stat, err := f.Stat()
	if err != nil {
		fmt.Println(err)
		return
	}
	fmt.Println(stat.ModTime())
}

What did you expect to see?

2021-03-08 11:03:38 +0800 CST

What did you see instead?

0001-01-01 00:00:00 +0000 UTC

@jayconrod jayconrod changed the title embed: Unable to get modtime to make net/http.ServeContent use 304 cache embed: modification time not set for embedded files Mar 8, 2021
@jayconrod
Copy link
Contributor

Closing as infeasible, unfortunately.

When modules are downloaded, their files are stripped of all metadata including timestamps and permission bits. That means embedded files from a module don't have that information. The only case where it would be feasible is when the embedded files are part of the main module, but then a program's behavior would subtly depend on how it was built.

This was discussed a bit in the embed proposal (#41191) (expand all comments, search for "timestamp"). The discussion there seems to recommend hashing and ETag for cache invalidation. However, ETag wasn't implemented in embed.FS or io/fs, and the proposal (#43223) was declined. It sounds like that may be reopened at some point when there's a clearer picture of how to implement that.

@jypelle
Copy link

jypelle commented Mar 28, 2021

I'm using this workaround to serve static files (css, js, ...):

type StaticFSWrapper struct {
	http.FileSystem
	FixedModTime time.Time
}

func (f *StaticFSWrapper) Open(name string) (http.File, error) {
	file, err := f.FileSystem.Open(name)

	return &StaticFileWrapper{File: file, fixedModTime: f.FixedModTime}, err
}

type StaticFileWrapper struct {
	http.File
	fixedModTime time.Time
}

func (f *StaticFileWrapper) Stat() (os.FileInfo, error) {

	fileInfo, err := f.File.Stat()

	return &StaticFileInfoWrapper{FileInfo: fileInfo, fixedModTime: f.fixedModTime}, err
}

type StaticFileInfoWrapper struct {
	os.FileInfo
	fixedModTime time.Time
}

func (f *StaticFileInfoWrapper) ModTime() time.Time {
	return f.fixedModTime
}

...

	// Static file handler
	//
	var staticFs     fs.FS
	// TODO: initialize staticFS with embed.FS var
	
	staticFileHandler := http.StripPrefix(
		"/static",
		http.FileServer(
			&StaticFSWrapper{
				FileSystem: http.FS(staticFs),
				FixedModTime: time.Now(),
			},
		),
	)
	d.router.PathPrefix("/static").HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
		w.Header().Set("Expires", "")
		w.Header().Set("Cache-Control", "no-cache, max-age=0")
		w.Header().Set("Pragma", "")
		staticFileHandler.ServeHTTP(w, r)
	})

Sign up for free to subscribe to this conversation on GitHub. Already have an account? Sign in.
Projects
None yet
Development

No branches or pull requests

4 participants