all repos — homestead @ 802d74fc1d38b7ee64f63c2f10810b20305c828a

Code for my website

make HTTP error an interface

Alan Pearce
commit

802d74fc1d38b7ee64f63c2f10810b20305c828a

parent

c0f110119e434f188f5959c48570df12121cc663

M domain/content/publisher/mux.godomain/content/publisher/mux.go
@@ -8,22 +8,19 @@ "alin.ovh/homestead/domain/web/templates"
ihttp "alin.ovh/homestead/shared/http" ) -func (app *App) Index(w http.ResponseWriter, _ *http.Request) *ihttp.Error { +func (app *App) Index(w http.ResponseWriter, _ *http.Request) ihttp.Error { err := pubtpl.IndexPage(app.siteSettings, templates.PageSettings{ Title: "Home", }).Render(w) if err != nil { - return &ihttp.Error{ - Code: http.StatusInternalServerError, - Message: "Failed to render index page", - } + return ihttp.InternalServerError("Failed to render index page", err) } return nil } -func (app *App) Style(w http.ResponseWriter, r *http.Request) *ihttp.Error { +func (app *App) Style(w http.ResponseWriter, r *http.Request) ihttp.Error { w.Header().Set("Content-Type", "text/css") http.ServeFileFS(w, r, templates.Files, "style.css")
M domain/identity/webfinger/service.godomain/identity/webfinger/service.go
@@ -51,7 +51,7 @@ func (s *Service) Handler() http.HandlerFunc {
return s.handleWebFinger } -func (s *Service) HandleFunc(w http.ResponseWriter, r *http.Request) *ihttp.Error { +func (s *Service) HandleFunc(w http.ResponseWriter, r *http.Request) ihttp.Error { resource := r.URL.Query().Get("resource") if resource == "" { return ihttp.BadRequest("Missing resource parameter", nil)
@@ -78,7 +78,7 @@ }
func (s *Service) handleWebFinger(w http.ResponseWriter, r *http.Request) { if err := s.HandleFunc(w, r); err != nil { - status := err.Code + status := err.StatusCode() if status == 0 { status = http.StatusInternalServerError }
M domain/web/mux.godomain/web/mux.go
@@ -15,19 +15,19 @@
"github.com/kevinpollet/nego" ) -func (website *Website) ErrorHandler(err *ihttp.Error, w http.ResponseWriter, r *http.Request) { +func (website *Website) ErrorHandler(err ihttp.Error, w http.ResponseWriter, r *http.Request) { if strings.Contains(r.Header.Get("Accept"), "text/html") { - w.WriteHeader(err.Code) + w.WriteHeader(err.StatusCode()) err := templates.Error(*website.siteSettings, err).Render(w) if err != nil { http.Error(w, err.Error(), http.StatusInternalServerError) } } else { - http.Error(w, err.Message, err.Code) + err.WriteHTTP(w) } } -func (website *Website) ServeHTTP(w http.ResponseWriter, r *http.Request) *ihttp.Error { +func (website *Website) ServeHTTP(w http.ResponseWriter, r *http.Request) ihttp.Error { urlPath := r.URL.Path if r.URL.Query().Has("go-get") && r.URL.Query().Get("go-get") == "1" { urlPath = "/go" + r.URL.Path
@@ -75,7 +75,7 @@
return nil } -func (website *Website) Calendar(w http.ResponseWriter, r *http.Request) *ihttp.Error { +func (website *Website) Calendar(w http.ResponseWriter, r *http.Request) ihttp.Error { website.counter.Count(r, "Calendar") err := calendar.CalendarPage(*website.siteSettings, templates.PageSettings{ Title: "Calendar",
@@ -83,11 +83,7 @@ TitleAttrs: templates.Attrs{},
BodyAttrs: templates.Attrs{}, }, *website.calendar).Render(w) if err != nil { - return &ihttp.Error{ - Code: http.StatusInternalServerError, - Message: "", - Cause: err, - } + return ihttp.InternalServerError("could not render calendar page", err) } return nil
@@ -101,7 +97,7 @@ re := regexp.MustCompile(
"^(.*)\\." + strings.ReplaceAll(website.config.WildcardDomain, ".", `\.`) + "$", ) replace := "${1}." + website.config.Domains[0] - mux.HandleFunc("/", func(w http.ResponseWriter, r *http.Request) *ihttp.Error { + mux.HandleFunc("/", func(w http.ResponseWriter, r *http.Request) ihttp.Error { switch { case r.URL.Query().Has("go-get") && r.URL.Query().Get("go-get") == "1": return website.ServeHTTP(w, r)
M domain/web/templates/error.godomain/web/templates/error.go
@@ -5,14 +5,14 @@ "strconv"
g "alin.ovh/gomponents" . "alin.ovh/gomponents/html" - http "alin.ovh/homestead/shared/http" + "alin.ovh/homestead/shared/http" ) -func Error(site SiteSettings, err *http.Error) g.Node { +func Error(site SiteSettings, err http.Error) g.Node { return Layout(site, PageSettings{ Title: "Error", }, Div( - H1(g.Text(strconv.Itoa(err.Code)+" "+err.Message)), + H1(g.Text(strconv.Itoa(err.StatusCode())+" "+err.Message())), H2(g.Text("ʕノ•ᴥ•ʔノ ︵ ┻━┻")), )) }
M domain/web/website.godomain/web/website.go
@@ -208,7 +208,7 @@ mux.HandleFunc("/calendar", website.Calendar)
website.identity.RegisterHandlers(mux) if opts.Development { - staticHandler := func(w http.ResponseWriter, r *http.Request) *ihttp.Error { + staticHandler := func(w http.ResponseWriter, r *http.Request) ihttp.Error { http.ServeFileFS(w, r, templates.Files, r.URL.Path) return nil
M shared/http/error.goshared/http/error.go
@@ -5,63 +5,91 @@ "fmt"
"net/http" ) -type Error struct { - Code int - Message string - Cause error +type Error interface { + error + StatusCode() int + Message() string + Unwrap() error + WriteHTTP(w http.ResponseWriter) } -func (e *Error) Error() string { - if e.Message == "" { - e.Message = http.StatusText(e.Code) +type httpError struct { + code int + message string + cause error +} + +func (e *httpError) Error() string { + if e.message == "" { + e.message = http.StatusText(e.code) } - return fmt.Sprintf("%d %s", e.Code, e.Message) + return fmt.Sprintf("%d %s", e.code, e.message) +} + +func (e *httpError) StatusCode() int { + return e.code } -func NewError(code int, message string, cause error) *Error { - return &Error{ - Code: code, - Message: message, - Cause: cause, +func (e *httpError) Message() string { + if e.message == "" { + e.message = http.StatusText(e.code) } + + return e.message } -func NotFound(message string) *Error { - return &Error{ - Code: http.StatusNotFound, - Message: message, +func (e *httpError) Unwrap() error { + return e.cause +} + +func (e *httpError) WriteHTTP(w http.ResponseWriter) { + http.Error(w, e.message, e.code) +} + +func NewError(code int, message string, cause error) Error { + return &httpError{ + code: code, + message: message, + cause: cause, } } -func BadRequest(message string, cause error) *Error { - return &Error{ - Code: http.StatusBadRequest, - Message: message, - Cause: cause, +func NotFound(message string) Error { + return &httpError{ + code: http.StatusNotFound, + message: message, } } -func InternalServerError(message string, cause error) *Error { - return &Error{ - Code: http.StatusInternalServerError, - Message: message, - Cause: cause, +func BadRequest(message string, cause error) Error { + return &httpError{ + code: http.StatusBadRequest, + message: message, + cause: cause, + } +} + +func InternalServerError(message string, cause error) Error { + return &httpError{ + code: http.StatusInternalServerError, + message: message, + cause: cause, } } -func Unauthorized(message string, cause error) *Error { - return &Error{ - Code: http.StatusUnauthorized, - Message: message, - Cause: cause, +func Unauthorized(message string, cause error) Error { + return &httpError{ + code: http.StatusUnauthorized, + message: message, + cause: cause, } } -func Forbidden(message string, cause error) *Error { - return &Error{ - Code: http.StatusForbidden, - Message: message, - Cause: cause, +func Forbidden(message string, cause error) Error { + return &httpError{ + code: http.StatusForbidden, + message: message, + cause: cause, } }
M shared/http/mux.goshared/http/mux.go
@@ -7,15 +7,15 @@ "alin.ovh/x/log"
) // HandleFunc is a function that handles an HTTP request and may return an Error. -type HandleFunc func(http.ResponseWriter, *http.Request) *Error +type HandleFunc func(http.ResponseWriter, *http.Request) Error // Handler is an interface for types that can serve HTTP requests and may return an Error. type Handler interface { - ServeHTTP(http.ResponseWriter, *http.Request) *Error + ServeHTTP(http.ResponseWriter, *http.Request) Error } // ErrorHandler is a function that handles HTTP errors. -type ErrorHandler func(*Error, http.ResponseWriter, *http.Request) +type ErrorHandler func(Error, http.ResponseWriter, *http.Request) // ServeMux is an HTTP request multiplexer with error handling support. // It matches the URL of each incoming request against a list of registered
@@ -30,8 +30,8 @@ // NewServeMux creates a new ServeMux with a default error handler.
func NewServeMux() *ServeMux { return &ServeMux{ ServeMux: http.NewServeMux(), - errorHandler: func(err *Error, w http.ResponseWriter, _ *http.Request) { - http.Error(w, err.Message, err.Code) + errorHandler: func(err Error, w http.ResponseWriter, _ *http.Request) { + err.WriteHTTP(w) }, } }