mirror of
https://github.com/tiennm99/noitu.git
synced 2026-10-11 03:13:45 +00:00
fix(wsapi): set cache headers on the served frontend
Assets under _app/immutable carry a content hash in the name, so a changed file is a changed URL and the old one can be cached forever. The shell cannot: it names those hashed assets, so a copy cached across a deploy points at files that no longer exist and the app loads into a blank page with nothing in the log.
This commit is contained in:
1 parent
64106735ec
commit
e5b03d041d
2 files changed
+60
No files matched your search
@@ -2,6 +2,9 @@ package wsapi
|
|||||||
|
|
||||||
import (
|
import (
|
||||||
"context"
|
"context"
|
||||||
|
"net/http"
|
||||||
|
"net/http/httptest"
|
||||||
|
"os"
|
||||||
"path/filepath"
|
"path/filepath"
|
||||||
"strings"
|
"strings"
|
||||||
"testing"
|
"testing"
|
||||||
@@ -376,3 +379,48 @@ func TestRoomCodesAreUnbiased(t *testing.T) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// TestStaticCacheHeaders pins the two cache policies the single-page bundle
|
||||||
|
// depends on. The failure they prevent is silent and total: an index.html
|
||||||
|
// cached across a deploy names hashed assets that no longer exist, so the app
|
||||||
|
// loads into a blank page with nothing in the log.
|
||||||
|
func TestStaticCacheHeaders(t *testing.T) {
|
||||||
|
dir := t.TempDir()
|
||||||
|
assets := filepath.Join(dir, "_app", "immutable", "chunks")
|
||||||
|
if err := os.MkdirAll(assets, 0o755); err != nil {
|
||||||
|
t.Fatal(err)
|
||||||
|
}
|
||||||
|
if err := os.WriteFile(filepath.Join(dir, "index.html"), []byte("<!doctype html>"), 0o644); err != nil {
|
||||||
|
t.Fatal(err)
|
||||||
|
}
|
||||||
|
if err := os.WriteFile(filepath.Join(assets, "abc123.js"), []byte("export{}"), 0o644); err != nil {
|
||||||
|
t.Fatal(err)
|
||||||
|
}
|
||||||
|
|
||||||
|
srv := NewServer(context.Background(), newTestDict("ngữ pháp", "pháp luật"), Config{WebDir: dir})
|
||||||
|
defer srv.Shutdown()
|
||||||
|
|
||||||
|
cases := []struct {
|
||||||
|
name string
|
||||||
|
path string
|
||||||
|
want string
|
||||||
|
}{
|
||||||
|
{"hashed asset", "/_app/immutable/chunks/abc123.js", "public, max-age=31536000, immutable"},
|
||||||
|
{"the shell", "/", "no-cache"},
|
||||||
|
{"a deep link falling back to the shell", "/play?difficulty=2", "no-cache"},
|
||||||
|
}
|
||||||
|
|
||||||
|
for _, tc := range cases {
|
||||||
|
t.Run(tc.name, func(t *testing.T) {
|
||||||
|
rec := httptest.NewRecorder()
|
||||||
|
srv.ServeHTTP(rec, httptest.NewRequest(http.MethodGet, tc.path, nil))
|
||||||
|
|
||||||
|
if rec.Code != http.StatusOK {
|
||||||
|
t.Fatalf("status = %d, want 200", rec.Code)
|
||||||
|
}
|
||||||
|
if got := rec.Header().Get("Cache-Control"); got != tc.want {
|
||||||
|
t.Errorf("Cache-Control = %q, want %q", got, tc.want)
|
||||||
|
}
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -87,6 +87,9 @@ func (s *Server) handleWS(w http.ResponseWriter, r *http.Request) {
|
|||||||
s.hub.expireToken(sess.resumeToken, s.cfg.GraceFor)
|
s.hub.expireToken(sess.resumeToken, s.cfg.GraceFor)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// immutablePrefix is where SvelteKit's adapter puts content-hashed assets.
|
||||||
|
const immutablePrefix = "/_app/immutable/"
|
||||||
|
|
||||||
// mountStatic serves the built frontend so one binary is the whole deployment.
|
// mountStatic serves the built frontend so one binary is the whole deployment.
|
||||||
//
|
//
|
||||||
// Unknown paths fall back to index.html because the frontend is a single-page
|
// Unknown paths fall back to index.html because the frontend is a single-page
|
||||||
@@ -116,9 +119,18 @@ func (s *Server) mountStatic() {
|
|||||||
return
|
return
|
||||||
}
|
}
|
||||||
if info, err := os.Stat(clean); err == nil && !info.IsDir() {
|
if info, err := os.Stat(clean); err == nil && !info.IsDir() {
|
||||||
|
// Everything under immutablePrefix carries a content hash in its
|
||||||
|
// name, so a changed file is a changed URL and the old one can be
|
||||||
|
// cached forever.
|
||||||
|
if strings.HasPrefix(r.URL.Path, immutablePrefix) {
|
||||||
|
w.Header().Set("Cache-Control", "public, max-age=31536000, immutable")
|
||||||
|
}
|
||||||
files.ServeHTTP(w, r)
|
files.ServeHTTP(w, r)
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
// The shell names those hashed assets, so a cached copy outlives the
|
||||||
|
// deploy that renamed them and the app loads into a blank page.
|
||||||
|
w.Header().Set("Cache-Control", "no-cache")
|
||||||
http.ServeFile(w, r, index)
|
http.ServeFile(w, r, index)
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in new issue
Block a user