about summary refs log tree commit diff stats
path: root/internal
diff options
context:
space:
mode:
Diffstat (limited to 'internal')
-rw-r--r--internal/website/filemap.go53
-rw-r--r--internal/website/mux.go16
2 files changed, 49 insertions, 20 deletions
diff --git a/internal/website/filemap.go b/internal/website/filemap.go
index c657848..126b67f 100644
--- a/internal/website/filemap.go
+++ b/internal/website/filemap.go
@@ -5,6 +5,7 @@ import (
 	"hash/fnv"
 	"io"
 	"io/fs"
+	"mime"
 	"os"
 	"path/filepath"
 	"strings"
@@ -15,11 +16,13 @@ import (
 )
 
 type File struct {
-	filename string
-	etag     string
+	filename     string
+	contentType  string
+	etag         string
+	alternatives map[string]string
 }
 
-var files = map[string]File{}
+var files = map[string]*File{}
 
 func hashFile(filename string) (string, error) {
 	f, err := os.Open(filename)
@@ -35,20 +38,34 @@ func hashFile(filename string) (string, error) {
 	return fmt.Sprintf(`W/"%x"`, hash.Sum(nil)), nil
 }
 
-func registerFile(urlpath string, filepath string) error {
-	if files[urlpath] != (File{}) {
-		log.Info("registerFile called with duplicate file", "url_path", urlpath)
+var encodings = map[string]string{
+	"br":   ".br",
+	"gzip": ".gz",
+}
 
-		return nil
-	}
-	hash, err := hashFile(filepath)
+func registerFile(urlpath string, fp string) error {
+	hash, err := hashFile(fp)
 	if err != nil {
 		return err
 	}
-	files[urlpath] = File{
-		filename: filepath,
-		etag:     hash,
+	f := File{
+		filename:     fp,
+		contentType:  mime.TypeByExtension(filepath.Ext(fp)),
+		etag:         hash,
+		alternatives: map[string]string{},
+	}
+	for enc, suffix := range encodings {
+		_, err := os.Stat(fp + suffix)
+		if err != nil {
+			if errors.Is(err, os.ErrNotExist) {
+				continue
+			} else {
+				return err
+			}
+		}
+		f.alternatives[enc] = fp + suffix
 	}
+	files[urlpath] = &f
 
 	return nil
 }
@@ -62,11 +79,15 @@ func registerContentFiles(root string) error {
 		if err != nil {
 			return errors.WithMessagef(err, "failed to make path relative, path: %s", filePath)
 		}
-		urlPath, _ := strings.CutSuffix(relPath, "index.html")
+		urlPath, _ := strings.CutSuffix("/"+relPath, "index.html")
 		if !f.IsDir() {
-			log.Debug("registering file", "urlpath", "/"+urlPath)
+			switch filepath.Ext(relPath) {
+			case ".br", ".gz":
+				return nil
+			}
+			log.Debug("registering file", "urlpath", urlPath)
 
-			return registerFile("/"+urlPath, filePath)
+			return registerFile(urlPath, filePath)
 		}
 
 		return nil
@@ -78,6 +99,6 @@ func registerContentFiles(root string) error {
 	return nil
 }
 
-func GetFile(urlPath string) File {
+func GetFile(urlPath string) *File {
 	return files[urlPath]
 }
diff --git a/internal/website/mux.go b/internal/website/mux.go
index 65a7e59..2d0a830 100644
--- a/internal/website/mux.go
+++ b/internal/website/mux.go
@@ -9,6 +9,7 @@ import (
 	"website/internal/log"
 
 	"github.com/benpate/digit"
+	"github.com/kevinpollet/nego"
 	"github.com/pkg/errors"
 )
 
@@ -22,7 +23,7 @@ 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+"/"] != (File{}) {
+	} else if !strings.HasSuffix(path, "/") && files[path+"/"] != nil {
 		cPath, differs = path+"/", true
 	}
 
@@ -67,7 +68,7 @@ func NewMux(cfg *config.Config, root string) (mux *http.ServeMux, err error) {
 			return nil
 		}
 		file := GetFile(urlPath)
-		if file == (File{}) {
+		if file == nil {
 			return &HTTPError{
 				Message: "File not found",
 				Code:    http.StatusNotFound,
@@ -79,8 +80,15 @@ func NewMux(cfg *config.Config, root string) (mux *http.ServeMux, err error) {
 		for k, v := range cfg.Extra.Headers {
 			w.Header().Add(k, v)
 		}
-
-		http.ServeFile(w, r, files[urlPath].filename)
+		enc := nego.NegotiateContentEncoding(r, "br", "gzip")
+		switch enc {
+		case "", "identity":
+			http.ServeFile(w, r, files[urlPath].filename)
+		case "br", "gzip":
+			w.Header().Add("Content-Encoding", enc)
+			w.Header().Add("Content-Type", file.contentType)
+			http.ServeFile(w, r, files[urlPath].alternatives[enc])
+		}
 
 		return nil
 	}))