internal/storage/files/file.go (view raw)
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 | package files
import (
"fmt"
"hash/fnv"
"io"
"mime"
"os"
"path/filepath"
"strings"
"gitlab.com/tozd/go/errors"
"go.alanpearce.eu/homestead/internal/buffer"
"go.alanpearce.eu/homestead/internal/storage"
)
var encodings = map[string]string{
"br": ".br",
"gzip": ".gz",
"zstd": ".zstd",
}
func (r *Reader) OpenFile(path string, filename string) (*storage.File, errors.E) {
f, err := os.Open(filename)
if err != nil {
return nil, errors.WithMessage(err, "could not open file for reading")
}
defer f.Close()
stat, err := f.Stat()
if err != nil {
return nil, errors.WithMessage(err, "could not stat file")
}
buf := new(buffer.Buffer)
if _, err := f.WriteTo(buf); err != nil {
return nil, errors.WithMessage(err, "could not read file")
}
etag, err := etag(buf)
if err != nil {
return nil, errors.WithMessage(err, "could not calculate etag")
}
file := &storage.File{
Path: path,
ContentType: mime.TypeByExtension(filepath.Ext(filename)),
LastModified: stat.ModTime(),
Etag: etag,
Encodings: map[string]*buffer.Buffer{
"identity": buf,
},
}
err = file.CalculateStyleHash()
if err != nil {
return nil, errors.WithStack(err)
}
for enc, suffix := range encodings {
_, err := os.Stat(filename + suffix)
if err != nil {
if errors.Is(err, os.ErrNotExist) {
continue
}
return nil, errors.WithMessagef(err, "could not stat file %s", filename+suffix)
}
bytes, err := os.ReadFile(filename + suffix)
if err != nil {
return nil, errors.WithMessagef(err, "could not read file %s", filename+suffix)
}
buf := buffer.NewBuffer(bytes)
file.Encodings[enc] = buf
}
return file, nil
}
func etag(f io.Reader) (string, errors.E) {
hash := fnv.New64a()
if _, err := io.Copy(hash, f); err != nil {
return "", errors.WithMessage(err, "could not hash file")
}
return fmt.Sprintf(`W/"%x"`, hash.Sum(nil)), nil
}
func pathNameToFileName(pathname string) string {
if strings.HasSuffix(pathname, "/") {
pathname += "index.html"
}
return strings.TrimPrefix(pathname, "/")
}
func cutSuffix(s, suffix string) string {
out, _ := strings.CutSuffix(s, suffix)
return out
}
func fileNameToPathName(filename string) string {
return cutSuffix(
cutSuffix(filename, "index.html"),
".html",
)
}
|