mirror of
https://github.com/tulir/gomuks.git
synced 2025-04-19 18:13:41 -05:00
server: add Cache-Control and ETag headers
This commit is contained in:
parent
b24a34ef97
commit
e243593e06
3 changed files with 40 additions and 4 deletions
|
@ -57,7 +57,7 @@ var ErrBadGateway = mautrix.RespError{
|
|||
StatusCode: http.StatusBadGateway,
|
||||
}
|
||||
|
||||
func (gmx *Gomuks) downloadMediaFromCache(ctx context.Context, w http.ResponseWriter, entry *database.Media, force bool) bool {
|
||||
func (gmx *Gomuks) downloadMediaFromCache(ctx context.Context, w http.ResponseWriter, r *http.Request, entry *database.Media, force bool) bool {
|
||||
if !entry.UseCache() {
|
||||
if force {
|
||||
mautrix.MNotFound.WithMessage("Media not found in cache").Write(w)
|
||||
|
@ -69,6 +69,9 @@ func (gmx *Gomuks) downloadMediaFromCache(ctx context.Context, w http.ResponseWr
|
|||
w.Header().Set("Mau-Cached-Error", "true")
|
||||
entry.Error.Write(w)
|
||||
return true
|
||||
} else if r.Header.Get("If-None-Match") == entry.ETag() {
|
||||
w.WriteHeader(http.StatusNotModified)
|
||||
return true
|
||||
}
|
||||
log := zerolog.Ctx(ctx)
|
||||
cacheFile, err := os.Open(gmx.cacheEntryToPath(entry.Hash[:]))
|
||||
|
@ -102,6 +105,8 @@ func cacheEntryToHeaders(w http.ResponseWriter, entry *database.Media) {
|
|||
w.Header().Set("Content-Length", strconv.FormatInt(entry.Size, 10))
|
||||
w.Header().Set("Content-Disposition", mime.FormatMediaType(entry.ContentDisposition(), map[string]string{"filename": entry.FileName}))
|
||||
w.Header().Set("Content-Security-Policy", "sandbox; default-src 'none'; script-src 'none';")
|
||||
w.Header().Set("Cache-Control", "max-age=2592000, immutable")
|
||||
w.Header().Set("ETag", entry.ETag())
|
||||
}
|
||||
|
||||
type noErrorWriter struct {
|
||||
|
@ -193,7 +198,7 @@ func (gmx *Gomuks) DownloadMedia(w http.ResponseWriter, r *http.Request) {
|
|||
return
|
||||
}
|
||||
|
||||
if gmx.downloadMediaFromCache(ctx, w, cacheEntry, false) {
|
||||
if gmx.downloadMediaFromCache(ctx, w, r, cacheEntry, false) {
|
||||
return
|
||||
}
|
||||
|
||||
|
@ -319,7 +324,7 @@ func (gmx *Gomuks) DownloadMedia(w http.ResponseWriter, r *http.Request) {
|
|||
return
|
||||
}
|
||||
if w != nil {
|
||||
gmx.downloadMediaFromCache(ctx, w, cacheEntry, true)
|
||||
gmx.downloadMediaFromCache(ctx, w, r, cacheEntry, true)
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -22,6 +22,7 @@ import (
|
|||
"encoding/base64"
|
||||
"encoding/json"
|
||||
"errors"
|
||||
"fmt"
|
||||
"io/fs"
|
||||
"net/http"
|
||||
_ "net/http/pprof"
|
||||
|
@ -64,7 +65,7 @@ func (gmx *Gomuks) StartServer() {
|
|||
if frontend, err := fs.Sub(web.Frontend, "dist"); err != nil {
|
||||
gmx.Log.Warn().Msg("Frontend not found")
|
||||
} else {
|
||||
router.Handle("/", http.FileServerFS(frontend))
|
||||
router.Handle("/", FrontendCacheMiddleware(http.FileServerFS(frontend)))
|
||||
}
|
||||
gmx.Server = &http.Server{
|
||||
Addr: gmx.Config.Web.ListenAddress,
|
||||
|
@ -79,6 +80,26 @@ func (gmx *Gomuks) StartServer() {
|
|||
gmx.Log.Info().Str("address", gmx.Config.Web.ListenAddress).Msg("Server started")
|
||||
}
|
||||
|
||||
func FrontendCacheMiddleware(next http.Handler) http.Handler {
|
||||
var frontendCacheETag string
|
||||
if Commit != "unknown" && !ParsedBuildTime.IsZero() {
|
||||
frontendCacheETag = fmt.Sprintf(`"%s-%s"`, Commit, ParsedBuildTime.Format(time.RFC3339))
|
||||
}
|
||||
return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
|
||||
if r.Header.Get("If-None-Match") == frontendCacheETag {
|
||||
w.WriteHeader(http.StatusNotModified)
|
||||
return
|
||||
}
|
||||
if strings.HasPrefix(r.URL.Path, "/assets/") {
|
||||
w.Header().Set("Cache-Control", "max-age=604800, immutable")
|
||||
}
|
||||
if frontendCacheETag != "" {
|
||||
w.Header().Set("ETag", frontendCacheETag)
|
||||
}
|
||||
next.ServeHTTP(w, r)
|
||||
})
|
||||
}
|
||||
|
||||
var (
|
||||
ErrInvalidHeader = mautrix.RespError{ErrCode: "FI.MAU.GOMUKS.INVALID_HEADER", StatusCode: http.StatusForbidden}
|
||||
ErrMissingCookie = mautrix.RespError{ErrCode: "FI.MAU.GOMUKS.MISSING_COOKIE", Err: "Missing gomuks_auth cookie", StatusCode: http.StatusUnauthorized}
|
||||
|
|
|
@ -9,6 +9,7 @@ package database
|
|||
import (
|
||||
"context"
|
||||
"database/sql"
|
||||
"fmt"
|
||||
"net/http"
|
||||
"slices"
|
||||
"time"
|
||||
|
@ -93,6 +94,8 @@ func (me *MediaError) Write(w http.ResponseWriter) {
|
|||
}
|
||||
me.Matrix.ExtraData["fi.mau.hicli.error_ts"] = me.ReceivedAt.UnixMilli()
|
||||
me.Matrix.ExtraData["fi.mau.hicli.next_retry_ts"] = me.ReceivedAt.Add(me.backoff()).UnixMilli()
|
||||
w.Header().Set("Mau-Errored-At", me.ReceivedAt.Format(http.TimeFormat))
|
||||
w.Header().Set("Cache-Control", fmt.Sprintf("max-age=%d", max(int(time.Until(me.ReceivedAt.Add(me.backoff())).Seconds()), 0)))
|
||||
me.Matrix.WithStatus(me.StatusCode).Write(w)
|
||||
}
|
||||
|
||||
|
@ -106,6 +109,13 @@ type Media struct {
|
|||
Error *MediaError
|
||||
}
|
||||
|
||||
func (m *Media) ETag() string {
|
||||
if m.Hash == nil {
|
||||
return ""
|
||||
}
|
||||
return fmt.Sprintf(`"%x"`, m.Hash)
|
||||
}
|
||||
|
||||
func (m *Media) UseCache() bool {
|
||||
return m != nil && (m.Hash != nil || m.Error.UseCache())
|
||||
}
|
||||
|
|
Loading…
Add table
Reference in a new issue