From 3bfcf5794f51f5001f8c40cd2d8b9e1a0f6792f1 Mon Sep 17 00:00:00 2001 From: Jeremy Baxter Date: Sat, 10 Jan 2026 21:27:43 +1300 Subject: [PATCH] server: use custom error page --- server/pages.go | 18 +++++++++++++++- server/server.go | 54 ++++++++++++++++++++++++++++-------------------- 2 files changed, 49 insertions(+), 23 deletions(-) diff --git a/server/pages.go b/server/pages.go index c527543..5bc4e72 100644 --- a/server/pages.go +++ b/server/pages.go @@ -31,7 +31,8 @@ type Page interface { func writePage(p Page, w http.ResponseWriter) { t, err := template.ParseFS(staticFS, "static/templates/" + p.SourceFile()) if err != nil { - http.Error(w, err.Error(), http.StatusInternalServerError) + http.Error(w, "Internal server error: " + err.Error(), + http.StatusInternalServerError) return } @@ -41,6 +42,21 @@ func writePage(p Page, w http.ResponseWriter) { } } +type ErrorPage struct { + Status string + Reason string +} + +func MakeErrorPage(status, reason string) (p ErrorPage) { + p.Status = status + p.Reason = reason + return +} + +func (p ErrorPage) SourceFile() string { return "error.html" } +func (p ErrorPage) Title() string { return "" } +func (p ErrorPage) Body() template.HTML { return template.HTML("") } + type IndexPage struct { Showcase string } diff --git a/server/server.go b/server/server.go index 3408f66..a0a8ca9 100644 --- a/server/server.go +++ b/server/server.go @@ -28,6 +28,16 @@ var artistPages map[string]map[string]ArtistPage // mapping of artist names to album names to pages var albumPages map[string]map[string]AlbumPage +func httpErrorReason(w http.ResponseWriter, error int, reason string) { + errorName := fmt.Sprintf("%d %s", error, http.StatusText(error)) + w.WriteHeader(error) + writePage(MakeErrorPage(errorName, reason), w) +} + +func httpError(w http.ResponseWriter, error int) { + httpErrorReason(w, error, "") +} + func doIndexPage(w http.ResponseWriter, req *http.Request) { writePage(indexPage, w) } @@ -64,13 +74,13 @@ func detectTextType(fileName string) string { func serveStaticFiles(w http.ResponseWriter, req *http.Request) { if strings.HasPrefix(req.RequestURI, "/static/templates/") { - http.Error(w, "forbidden", http.StatusForbidden) + httpError(w, http.StatusForbidden) return } contents, err := staticFS.ReadFile(req.RequestURI[1:]) if err != nil { - http.Error(w, err.Error(), http.StatusNotFound) + httpError(w, http.StatusNotFound) return } @@ -86,30 +96,31 @@ func serveStaticFiles(w http.ResponseWriter, req *http.Request) { func serveMediaDirectory(w http.ResponseWriter, req *http.Request) { path, err := url.QueryUnescape("/" + strings.SplitN(req.RequestURI[1:], "/", 2)[1]) if err != nil { - http.Error(w, "bad request", http.StatusBadRequest) + httpError(w, http.StatusBadRequest) return } filePath := musicindex.MediaDirectory() + path - if strings.Contains(path, "/..") || strings.Contains(path, "/.") { - http.Error(w, "forbidden", http.StatusForbidden) + if strings.Contains(path, "/../") || strings.Contains(path, "/./") { + httpError(w, http.StatusForbidden) return } stat, err := os.Stat(filePath) if err != nil { - http.Error(w, "not found", http.StatusNotFound) + httpError(w, http.StatusNotFound) return } if stat.IsDir() { - http.Error(w, "is a directory; maybe you are looking for " + path, http.StatusForbidden) + httpErrorReason(w, http.StatusForbidden, + "is a directory; maybe you are looking for " + path) return } f, err := os.Open(filePath) if err != nil { - http.Error(w, "not found", http.StatusNotFound) + httpError(w, http.StatusNotFound) return } defer f.Close() @@ -120,14 +131,14 @@ func handleArtistAlbumPage(w http.ResponseWriter, req *http.Request) { // test artist rx, err := regexp.Compile("^/([^/?]+)/?(\\?.*)?$") if err != nil { - http.Error(w, "internal server error", http.StatusInternalServerError) + httpError(w, http.StatusInternalServerError) return } if (rx.MatchString(req.RequestURI)) { artist, err := url.QueryUnescape(rx.FindStringSubmatch(req.RequestURI)[1]) if err != nil { - http.Error(w, "bad request", http.StatusBadRequest) + httpError(w, http.StatusBadRequest) return } if musicindex.ArtistExists(artist) { @@ -142,14 +153,14 @@ func handleArtistAlbumPage(w http.ResponseWriter, req *http.Request) { } // artist not found - http.Error(w, "not found", http.StatusNotFound) + httpError(w, http.StatusNotFound) return } // test album tarball rx, err = regexp.Compile("^/([^/?]+)/([^/?]+)\\.tar\\.gz$") if err != nil { - http.Error(w, "internal server error", http.StatusInternalServerError) + httpError(w, http.StatusInternalServerError) return } @@ -157,12 +168,12 @@ func handleArtistAlbumPage(w http.ResponseWriter, req *http.Request) { captures := rx.FindStringSubmatch(req.RequestURI) artist, err := url.QueryUnescape(captures[1]) if err != nil { - http.Error(w, "bad request", http.StatusBadRequest) + httpError(w, http.StatusBadRequest) return } albumName, err := url.QueryUnescape(captures[2]) if err != nil { - http.Error(w, "bad request", http.StatusBadRequest) + httpError(w, http.StatusBadRequest) return } @@ -171,8 +182,7 @@ func handleArtistAlbumPage(w http.ResponseWriter, req *http.Request) { if ok { f, err := os.Open(album.Tarball) if err != nil { - http.Error(w, "internal server error: " + err.Error(), - http.StatusInternalServerError) + httpErrorReason(w, http.StatusInternalServerError, err.Error()) return } defer f.Close() @@ -187,14 +197,14 @@ func handleArtistAlbumPage(w http.ResponseWriter, req *http.Request) { } // artist or album not found - http.Error(w, "not found", http.StatusNotFound) + httpError(w, http.StatusNotFound) return } // test album rx, err = regexp.Compile("^/([^/?]+)/([^/?]+)/?(\\?.*)?$") if err != nil { - http.Error(w, "internal server error", http.StatusInternalServerError) + httpErrorReason(w, http.StatusInternalServerError, err.Error()) return } @@ -202,12 +212,12 @@ func handleArtistAlbumPage(w http.ResponseWriter, req *http.Request) { captures := rx.FindStringSubmatch(req.RequestURI) artist, err := url.QueryUnescape(captures[1]) if err != nil { - http.Error(w, "bad request", http.StatusBadRequest) + httpError(w, http.StatusBadRequest) return } albumName, err := url.QueryUnescape(captures[2]) if err != nil { - http.Error(w, "bad request", http.StatusBadRequest) + httpError(w, http.StatusBadRequest) return } @@ -220,12 +230,12 @@ func handleArtistAlbumPage(w http.ResponseWriter, req *http.Request) { } // artist or album not found - http.Error(w, "not found", http.StatusNotFound) + httpError(w, http.StatusNotFound) return } // illegal URI format - http.Error(w, "not found", http.StatusNotFound) + httpError(w, http.StatusNotFound) } func RunOn(address string) {