http: Error template documents are supported

This commit is contained in:
Sasha Koshka 2024-12-07 03:10:45 -05:00
parent 62885b2e37
commit a7b360cc09

View File

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