diff --git a/http/handler.go b/http/handler.go index 3a32332..8293b35 100644 --- a/http/handler.go +++ b/http/handler.go @@ -11,11 +11,18 @@ import "path/filepath" import "git.tebibyte.media/sashakoshka/step" import "git.tebibyte.media/sashakoshka/goutil/container" +type ErrorData struct { + Status int + Message any +} + type Handler struct { Environment *step.Environment Directories bool StepExt ucontainer.Set[string] Index []string + ErrorDocument string + DirectoryDocument string } func (this *Handler) ServeHTTP (res http.ResponseWriter, req *http.Request) { @@ -39,9 +46,7 @@ func (this *Handler) ServeHTTP (res http.ResponseWriter, req *http.Request) { info, err := statFile(filesystem, pathToName(pat)) if err != nil { - // TODO need more detailed error, should allow specifying error - // documents - this.serveError(res, req, http.StatusNotFound, req.URL) + this.serveError(res, req, http.StatusNotFound, req.URL, false) return } if info.IsDir() { @@ -57,12 +62,12 @@ func (this *Handler) ServeHTTP (res http.ResponseWriter, req *http.Request) { info, err := statFile(filesystem, pathToName(currentPath)) if err != nil { continue } if info.IsDir() { continue } - this.serveFile(res, req, filesystem, currentPath) + this.serveFile(res, req, currentPath) return } if !this.Directories { - this.serveError(res, req, http.StatusForbidden, req.URL) + this.serveError(res, req, http.StatusForbidden, req.URL, false) return } @@ -72,21 +77,32 @@ func (this *Handler) ServeHTTP (res http.ResponseWriter, req *http.Request) { // as data } - this.serveFile(res, req, filesystem, pat) + this.serveFile(res, req, pat) } -func (this *Handler) serveFile (res http.ResponseWriter, req *http.Request, filesystem fs.FS, pat string) { +func (this *Handler) serveFile ( + res http.ResponseWriter, + req *http.Request, + pat string, +) { name := pathToName(pat) if !this.StepExt.Has(filepath.Ext(name)) { // just a normal file http.ServeFileFS(res, req, this.Environment.GetFS(), name) return } + this.serveDocument(res, req, name) +} +func (this *Handler) serveDocument ( + res http.ResponseWriter, + req *http.Request, + name string, +) { // parse document, err := this.Environment.Parse(name) if err != nil { - this.serveError(res, req, http.StatusInternalServerError, err) + this.serveError(res, req, http.StatusInternalServerError, err, false) return } @@ -115,7 +131,7 @@ func (this *Handler) serveFile (res http.ResponseWriter, req *http.Request, file Data: data, }) if err != nil { - this.serveError(res, req, http.StatusInternalServerError, err) + this.serveError(res, req, http.StatusInternalServerError, err, false) return } @@ -126,16 +142,41 @@ func (this *Handler) serveFile (res http.ResponseWriter, req *http.Request, file } } -func (this *Handler) serveError (res http.ResponseWriter, req *http.Request, status int, message any) { - res.Header().Add("Content-Type", "text/plain") - res.WriteHeader(status) - // TODO: allow customization with templates - if message == nil { - fmt.Fprintf(res, "%d %s\n", status, http.StatusText(status)) - } else { - fmt.Fprintf(res, "%d %s: %v\n", status, http.StatusText(status), message) - } +func (this *Handler) serveError ( + res http.ResponseWriter, + req *http.Request, + status int, + message any, + safeMode bool, +) { log.Printf("ERR %d %s: %v\n", status, http.StatusText(status), message) + if safeMode || this.ErrorDocument == "" { + res.Header().Add("Content-Type", "text/plain") + res.WriteHeader(status) + // TODO: allow customization with templates + if message == nil { + fmt.Fprintf(res, "%d %s\n", status, http.StatusText(status)) + } else { + fmt.Fprintf(res, "%d %s: %v\n", status, http.StatusText(status), message) + } + return + } + + document, err := this.Environment.Parse(this.ErrorDocument) + if err != nil { + this.serveError(res, req, http.StatusInternalServerError, err, true) + return + } + err = document.Execute(res, step.ExecutionData { + Data: ErrorData { + Status: status, + Message: message, + }, + }) + if err != nil { + this.serveError(res, req, http.StatusInternalServerError, err, true) + return + } } func (this *Handler) logErr (name string, err error) {