Commit e5973a00dd
Verified · cmc ci/build: success ci/test: success ci/vuln: success
Layout: unified · split
internal/httpd/fonts_test.go added +45
| @@ -0,0 +1,45 @@ | |||
| 1 | package httpd | ||
| 2 | |||
| 3 | import ( | ||
| 4 | "net/http" | ||
| 5 | "net/http/httptest" | ||
| 6 | "regexp" | ||
| 7 | "testing" | ||
| 8 | |||
| 9 | "gitbay.org/gitbay/internal/config" | ||
| 10 | "gitbay.org/gitbay/internal/web" | ||
| 11 | ) | ||
| 12 | |||
| 13 | // TestStylesheetFontsAreServed: every font URL the embedded stylesheet asks | ||
| 14 | // for has a route, and that route answers with the file. The route names | ||
| 15 | // and the @font-face URLs were once maintained by hand and drifted, so | ||
| 16 | // gitbay.org served no web font at all (#102). | ||
| 17 | func TestStylesheetFontsAreServed(t *testing.T) { | ||
| 18 | s := New(config.Default(), nil) | ||
| 19 | byPattern := map[string]http.HandlerFunc{} | ||
| 20 | for _, r := range s.Routes() { | ||
| 21 | if r.Method == "GET" { | ||
| 22 | byPattern[r.Pattern] = r.Handler | ||
| 23 | } | ||
| 24 | } | ||
| 25 | urls := regexp.MustCompile(`url\((/static/fonts/[^)]+)\)`).FindAllSubmatch(web.StyleCSS, -1) | ||
| 26 | if len(urls) == 0 { | ||
| 27 | t.Fatal("stylesheet declares no font URLs") | ||
| 28 | } | ||
| 29 | for _, m := range urls { | ||
| 30 | u := string(m[1]) | ||
| 31 | h, ok := byPattern[u] | ||
| 32 | if !ok { | ||
| 33 | t.Errorf("%s: stylesheet asks for it, route table has no route", u) | ||
| 34 | continue | ||
| 35 | } | ||
| 36 | rec := httptest.NewRecorder() | ||
| 37 | h(rec, httptest.NewRequest("GET", u, nil)) | ||
| 38 | if rec.Code != http.StatusOK || rec.Body.Len() == 0 { | ||
| 39 | t.Errorf("%s: status %d, %d bytes", u, rec.Code, rec.Body.Len()) | ||
| 40 | } | ||
| 41 | if ct := rec.Header().Get("Content-Type"); ct != "font/woff2" { | ||
| 42 | t.Errorf("%s: content-type %q", u, ct) | ||
| 43 | } | ||
| 44 | } | ||
| 45 | } | ||
internal/httpd/routes.go +12 −5
| @@ -2,9 +2,12 @@ package httpd | |||
| 2 | 2 | ||
| 3 | import ( | 3 | import ( |
| 4 | "fmt" | 4 | "fmt" |
| 5 | "io/fs" | ||
| 5 | "net" | 6 | "net" |
| 6 | "net/http" | 7 | "net/http" |
| 7 | "strings" | 8 | "strings" |
| 9 | |||
| 10 | "gitbay.org/gitbay/internal/web" | ||
| 8 | ) | 11 | ) |
| 9 | 12 | ||
| 10 | // Route is one entry in the explicit route table. The view-only guarantee is | 13 | // Route is one entry in the explicit route table. The view-only guarantee is |
| @@ -41,11 +44,15 @@ func (s *Server) Routes() []Route { | |||
| 41 | Route{Method: "GET", Pattern: "/healthz", Handler: s.healthz}, | 44 | Route{Method: "GET", Pattern: "/healthz", Handler: s.healthz}, |
| 42 | Route{Method: "GET", Pattern: "/privacy", Handler: s.privacy}, | 45 | Route{Method: "GET", Pattern: "/privacy", Handler: s.privacy}, |
| 43 | Route{Method: "GET", Pattern: "/static/style.css", Handler: s.stylesheet}, | 46 | Route{Method: "GET", Pattern: "/static/style.css", Handler: s.stylesheet}, |
| 44 | // Literal per-file routes: a {name} wildcard is ambiguous against | 47 | ) |
| 45 | // /{owner}/{repo}/... patterns in ServeMux precedence. | 48 | // One literal route per embedded font file: a {name} wildcard is |
| 46 | Route{Method: "GET", Pattern: "/static/fonts/plex-sans.woff2", Handler: s.font}, | 49 | // ambiguous against /{owner}/{repo}/... patterns in ServeMux precedence, |
| 47 | Route{Method: "GET", Pattern: "/static/fonts/plex-mono-400.woff2", Handler: s.font}, | 50 | // and hand-typed names drifted from the files once (#102). |
| 48 | Route{Method: "GET", Pattern: "/static/fonts/plex-mono-500.woff2", Handler: s.font}, | 51 | fonts, _ := fs.ReadDir(web.FontFS, "static/fonts") |
| 52 | for _, f := range fonts { | ||
| 53 | routes = append(routes, Route{Method: "GET", Pattern: "/static/fonts/" + f.Name(), Handler: s.font}) | ||
| 54 | } | ||
| 55 | routes = append(routes, | ||
| 49 | Route{Method: "GET", Pattern: "/favicon.svg", Handler: s.favicon}, | 56 | Route{Method: "GET", Pattern: "/favicon.svg", Handler: s.favicon}, |
| 50 | Route{Method: "GET", Pattern: "/{owner}", Handler: s.ownerPage}, | 57 | Route{Method: "GET", Pattern: "/{owner}", Handler: s.ownerPage}, |
| 51 | Route{Method: "GET", Pattern: "/{owner}/{repo}", Handler: s.repoHome}, | 58 | Route{Method: "GET", Pattern: "/{owner}/{repo}", Handler: s.repoHome}, |