diff options
-rw-r--r-- | internal/server/server.go | 12 | ||||
-rw-r--r-- | internal/storage/files/reader.go (renamed from internal/website/filemap.go) | 66 | ||||
-rw-r--r-- | internal/website/mux.go | 37 |
3 files changed, 69 insertions, 46 deletions
diff --git a/internal/server/server.go b/internal/server/server.go index b5526a0..e4ff63b 100644 --- a/internal/server/server.go +++ b/internal/server/server.go @@ -16,6 +16,7 @@ import ( "go.alanpearce.eu/website/internal/builder" cfg "go.alanpearce.eu/website/internal/config" + "go.alanpearce.eu/website/internal/storage/files" "go.alanpearce.eu/website/internal/vcs" "go.alanpearce.eu/website/internal/website" "go.alanpearce.eu/x/log" @@ -174,7 +175,14 @@ func New(runtimeConfig *Config, log *log.Logger) (*Server, error) { } loggingMux := http.NewServeMux() - mux, err := website.NewMux(config, runtimeConfig.Root, log.Named("website")) + + log.Debug("registering content files", "root", runtimeConfig.Root) + reader, err := files.NewReader(runtimeConfig.Root, log.Named("files")) + if err != nil { + return nil, errors.WithMessage(err, "could not create file reader") + } + + mux, err := website.NewMux(config, reader, log.Named("website")) if err != nil { return nil, errors.WithMessage(err, "could not create website mux") } @@ -188,7 +196,7 @@ func New(runtimeConfig *Config, log *log.Logger) (*Server, error) { loggingMux.HandleFunc("/", func(w http.ResponseWriter, r *http.Request) { switch { case slices.Contains(config.Domains, r.Host): - path, _ := website.CanonicalisePath(r.URL.Path) + path, _ := reader.CanonicalisePath(r.URL.Path) http.Redirect( w, r, diff --git a/internal/website/filemap.go b/internal/storage/files/reader.go index edef7cd..0bad9ef 100644 --- a/internal/website/filemap.go +++ b/internal/storage/files/reader.go @@ -1,4 +1,4 @@ -package website +package files import ( "fmt" @@ -16,21 +16,38 @@ import ( ) type File struct { - contentType string - etag string - alternatives map[string]string + ContentType string + Etag string + Alternatives map[string]string } func (f *File) AvailableEncodings() []string { encs := []string{} - for enc := range f.alternatives { + for enc := range f.Alternatives { encs = append(encs, enc) } return encs } -var files = map[string]*File{} +type Reader struct { + root string + log *log.Logger + files map[string]*File +} + +func NewReader(path string, log *log.Logger) (*Reader, error) { + r := &Reader{ + root: path, + log: log, + files: make(map[string]*File), + } + if err := r.registerContentFiles(); err != nil { + return nil, errors.WithMessagef(err, "registering content files") + } + + return r, nil +} func hashFile(filename string) (string, error) { f, err := os.Open(filename) @@ -51,15 +68,15 @@ var encodings = map[string]string{ "gzip": ".gz", } -func registerFile(urlpath string, fp string) error { +func (r *Reader) registerFile(urlpath string, fp string) error { hash, err := hashFile(fp) if err != nil { return err } f := File{ - contentType: mime.TypeByExtension(filepath.Ext(fp)), - etag: hash, - alternatives: map[string]string{ + ContentType: mime.TypeByExtension(filepath.Ext(fp)), + Etag: hash, + Alternatives: map[string]string{ "identity": fp, }, } @@ -72,19 +89,19 @@ func registerFile(urlpath string, fp string) error { return err } - f.alternatives[enc] = fp + suffix + f.Alternatives[enc] = fp + suffix } - files[urlpath] = &f + r.files[urlpath] = &f return nil } -func registerContentFiles(root string, log *log.Logger) error { - err := filepath.WalkDir(root, func(filePath string, f fs.DirEntry, err error) error { +func (r *Reader) registerContentFiles() error { + err := filepath.WalkDir(r.root, func(filePath string, f fs.DirEntry, err error) error { if err != nil { return errors.WithMessagef(err, "failed to access path %s", filePath) } - relPath, err := filepath.Rel(root, filePath) + relPath, err := filepath.Rel(r.root, filePath) if err != nil { return errors.WithMessagef(err, "failed to make path relative, path: %s", filePath) } @@ -94,9 +111,9 @@ func registerContentFiles(root string, log *log.Logger) error { case ".br", ".gz": return nil } - log.Debug("registering file", "urlpath", urlPath) + r.log.Debug("registering file", "urlpath", urlPath) - return registerFile(urlPath, filePath) + return r.registerFile(urlPath, filePath) } return nil @@ -108,6 +125,17 @@ func registerContentFiles(root string, log *log.Logger) error { return nil } -func GetFile(urlPath string) *File { - return files[urlPath] +func (r *Reader) GetFile(urlPath string) *File { + return r.files[urlPath] +} + +func (r *Reader) CanonicalisePath(path string) (cPath string, differs bool) { + cPath = path + if strings.HasSuffix(path, "/index.html") { + cPath, differs = strings.CutSuffix(path, "index.html") + } else if !strings.HasSuffix(path, "/") && r.files[path+"/"] != nil { + cPath, differs = path+"/", true + } + + return cPath, differs } diff --git a/internal/website/mux.go b/internal/website/mux.go index 6844551..5f571f8 100644 --- a/internal/website/mux.go +++ b/internal/website/mux.go @@ -7,25 +7,14 @@ import ( "go.alanpearce.eu/website/internal/config" ihttp "go.alanpearce.eu/website/internal/http" - "go.alanpearce.eu/x/log" + "go.alanpearce.eu/website/internal/storage/files" "go.alanpearce.eu/website/templates" + "go.alanpearce.eu/x/log" "github.com/benpate/digit" "github.com/kevinpollet/nego" - "gitlab.com/tozd/go/errors" ) -func CanonicalisePath(path string) (cPath string, differs bool) { - cPath = path - if strings.HasSuffix(path, "/index.html") { - cPath, differs = strings.CutSuffix(path, "index.html") - } else if !strings.HasSuffix(path, "/") && files[path+"/"] != nil { - cPath, differs = path+"/", true - } - - return cPath, differs -} - type webHandler func(http.ResponseWriter, *http.Request) *ihttp.Error type WrappedWebHandler struct { @@ -62,31 +51,29 @@ func (fn WrappedWebHandler) ServeHTTP(w http.ResponseWriter, r *http.Request) { } } -func NewMux(cfg *config.Config, root string, log *log.Logger) (mux *http.ServeMux, err error) { +func NewMux( + cfg *config.Config, + reader *files.Reader, + log *log.Logger, +) (mux *http.ServeMux, err error) { mux = &http.ServeMux{} - - log.Debug("registering content files", "root", root) - err = registerContentFiles(root, log) - if err != nil { - return nil, errors.WithMessagef(err, "registering content files") - } templates.Setup() mux.Handle("/", wrapHandler(cfg, func(w http.ResponseWriter, r *http.Request) *ihttp.Error { - urlPath, shouldRedirect := CanonicalisePath(r.URL.Path) + urlPath, shouldRedirect := reader.CanonicalisePath(r.URL.Path) if shouldRedirect { http.Redirect(w, r, urlPath, 302) return nil } - file := GetFile(urlPath) + file := reader.GetFile(urlPath) if file == nil { return &ihttp.Error{ Message: "File not found", Code: http.StatusNotFound, } } - w.Header().Add("ETag", file.etag) + w.Header().Add("ETag", file.Etag) w.Header().Add("Vary", "Accept-Encoding") w.Header().Add("Content-Security-Policy", cfg.CSP.String()) for k, v := range cfg.Extra.Headers { @@ -96,9 +83,9 @@ func NewMux(cfg *config.Config, root string, log *log.Logger) (mux *http.ServeMu switch enc { case "br", "gzip": w.Header().Add("Content-Encoding", enc) - w.Header().Add("Content-Type", file.contentType) + w.Header().Add("Content-Type", file.ContentType) } - http.ServeFile(w, r, files[urlPath].alternatives[enc]) + http.ServeFile(w, r, file.Alternatives[enc]) return nil }, log)) |