about summary refs log tree commit diff stats
diff options
context:
space:
mode:
authorAlan Pearce2024-06-23 19:22:14 +0200
committerAlan Pearce2024-06-23 19:23:20 +0200
commitb6724988aa748c78db3954abf59712ede0a49063 (patch)
tree7d34bfea43b90f8f4e493e746b3b89ebce284557
parent8cca5de5d0cd6b18c328a18de286eeb3b2c75b84 (diff)
downloadwebsite-b6724988aa748c78db3954abf59712ede0a49063.tar.lz
website-b6724988aa748c78db3954abf59712ede0a49063.tar.zst
website-b6724988aa748c78db3954abf59712ede0a49063.zip
serve pre-compressed files according to accept-encoding
-rw-r--r--go.mod3
-rw-r--r--go.sum6
-rw-r--r--internal/website/filemap.go53
-rw-r--r--internal/website/mux.go16
-rw-r--r--nix/gomod2nix.toml7
5 files changed, 60 insertions, 25 deletions
diff --git a/go.mod b/go.mod
index 989a82c..7d6d42b 100644
--- a/go.mod
+++ b/go.mod
@@ -17,6 +17,7 @@ require (
 	github.com/deckarep/golang-set/v2 v2.6.0
 	github.com/fatih/structtag v1.2.0
 	github.com/fsnotify/fsnotify v1.7.0
+	github.com/kevinpollet/nego v0.0.0-20211010160919-a65cd48cee43
 	github.com/osdevisnot/sorvor v0.4.4
 	github.com/pkg/errors v0.9.1
 	github.com/snabb/sitemap v1.0.4
@@ -45,6 +46,6 @@ require (
 	github.com/snabb/diagio v1.0.4 // indirect
 	go.uber.org/multierr v1.11.0 // indirect
 	golang.org/x/sys v0.20.0 // indirect
-	golang.org/x/text v0.15.0 // indirect
+	golang.org/x/text v0.16.0 // indirect
 	gopkg.in/yaml.v2 v2.4.0 // indirect
 )
diff --git a/go.sum b/go.sum
index 8fc9606..b7b12b7 100644
--- a/go.sum
+++ b/go.sum
@@ -57,6 +57,8 @@ github.com/google/go-cmp v0.6.0/go.mod h1:17dUlkBOakJ0+DkrSSNjCkIjxS6bF9zb3elmeN
 github.com/google/renameio v0.1.0/go.mod h1:KWCgfxg9yswjAJkECMjeO8J8rahYeXnNhOm40UhjYkI=
 github.com/gorilla/css v1.0.1 h1:ntNaBIghp6JmvWnxbZKANoLyuXTPZ4cAMlo6RyhlbO8=
 github.com/gorilla/css v1.0.1/go.mod h1:BvnYkspnSzMmwRK+b8/xgNPLiIuNZr6vbZBTPQ2A3b0=
+github.com/kevinpollet/nego v0.0.0-20211010160919-a65cd48cee43 h1:Pdirg1gwhEcGjMLyuSxGn9664p+P8J9SrfMgpFwrDyg=
+github.com/kevinpollet/nego v0.0.0-20211010160919-a65cd48cee43/go.mod h1:ahLMuLCUyDdXqtqGyuwGev7/PGtO7r7ocvdwDuEN/3E=
 github.com/kisielk/gotool v1.0.0/go.mod h1:XhKaO+MFFWcvkIS/tQcRk01m1F5IRFswLeQ+oQHNcck=
 github.com/kr/pretty v0.1.0/go.mod h1:dAy3ld7l9f0ibDNOQOHHMYYIIbhfbHSm3C4ZsoJORNo=
 github.com/kr/pretty v0.3.1 h1:flRD4NNwYAUpkphVc1HcthR4KEIFJ65n8Mw5qdRn3LE=
@@ -146,8 +148,8 @@ golang.org/x/text v0.3.3/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ=
 golang.org/x/text v0.3.7/go.mod h1:u+2+/6zg+i71rQMx5EYifcz6MCKuco9NR6JIITiCfzQ=
 golang.org/x/text v0.7.0/go.mod h1:mrYo+phRRbMaCq/xk9113O4dZlRixOauAjOtrjsXDZ8=
 golang.org/x/text v0.9.0/go.mod h1:e1OnstbJyHTd6l/uOt8jFFHp6TRDWZR/bV3emEE/zU8=
-golang.org/x/text v0.15.0 h1:h1V/4gjBv8v9cjcR6+AR5+/cIYK5N/WAgiv4xlsEtAk=
-golang.org/x/text v0.15.0/go.mod h1:18ZOQIKpY8NJVqYksKHtTdi31H5itFRjB5/qKTNYzSU=
+golang.org/x/text v0.16.0 h1:a94ExnEXNtEwYLGJSIUxnWoxoRz/ZcCsV63ROupILh4=
+golang.org/x/text v0.16.0/go.mod h1:GhwF1Be+LQoKShO3cGOHzqOgRrGaYc9AvblQOmPVHnI=
 golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ=
 golang.org/x/tools v0.0.0-20190311212946-11955173bddd/go.mod h1:LCzVGOaR6xXOjkQ3onu1FJEFr0SW1gC7cKk1uF8kGRs=
 golang.org/x/tools v0.0.0-20190621195816-6e04913cbbac/go.mod h1:/rFqwRUd4F7ZHNgwSSTFct+R/Kf4OFW1sUzUTQQTgfc=
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
 	}))
diff --git a/nix/gomod2nix.toml b/nix/gomod2nix.toml
index 459fc49..f797587 100644
--- a/nix/gomod2nix.toml
+++ b/nix/gomod2nix.toml
@@ -74,6 +74,9 @@ schema = 3
   [mod."github.com/gorilla/css"]
     version = "v1.0.1"
     hash = "sha256-6JwNHqlY2NpZ0pSQTyYPSpiNqjXOdFHqrUT10sv3y8A="
+  [mod."github.com/kevinpollet/nego"]
+    version = "v0.0.0-20211010160919-a65cd48cee43"
+    hash = "sha256-pmlOiyybXPtEDi/QYAA8rXN3wf12/Zd2rheC9D47x0g="
   [mod."github.com/microcosm-cc/bluemonday"]
     version = "v1.0.26"
     hash = "sha256-ZX4QUWHVEoGBeTHfPcLD5XoiubeO8GhkdqkC4Me8nRE="
@@ -114,8 +117,8 @@ schema = 3
     version = "v0.20.0"
     hash = "sha256-mowlaoG2k4n1c1rApWef5EMiXd3I77CsUi8jPh6pTYA="
   [mod."golang.org/x/text"]
-    version = "v0.15.0"
-    hash = "sha256-pBnj0AEkfkvZf+3bN7h6epCD2kurw59clDP7yWvxKlk="
+    version = "v0.16.0"
+    hash = "sha256-hMTO45upjEuA4sJzGplJT+La2n3oAfHccfYWZuHcH+8="
   [mod."gopkg.in/yaml.v2"]
     version = "v2.4.0"
     hash = "sha256-uVEGglIedjOIGZzHW4YwN1VoRSTK8o0eGZqzd+TNdd0="