about summary refs log tree commit diff stats
diff options
context:
space:
mode:
-rw-r--r--internal/server/server.go12
-rw-r--r--internal/storage/files/reader.go (renamed from internal/website/filemap.go)66
-rw-r--r--internal/website/mux.go37
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))