server: use custom error page

This commit is contained in:
Jeremy Baxter 2026-01-10 21:27:43 +13:00
parent dcb8094403
commit 3bfcf5794f
2 changed files with 49 additions and 23 deletions

View file

@ -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
}

View file

@ -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) {