109 lines
3.5 KiB
Go
109 lines
3.5 KiB
Go
package hws
|
|
|
|
import (
|
|
"context"
|
|
"io"
|
|
"net/http"
|
|
"net/http/httptest"
|
|
|
|
"github.com/pkg/errors"
|
|
)
|
|
|
|
// Error to use with Server.ThrowError
|
|
type HWSError struct {
|
|
StatusCode int // HTTP Status code
|
|
Message string // Error message
|
|
Error error // Error
|
|
Level ErrorLevel // Error level to use for logging. Defaults to Error
|
|
RenderErrorPage bool // If true, the servers ErrorPage will be rendered
|
|
}
|
|
|
|
type ErrorLevel string
|
|
|
|
const (
|
|
ErrorDEBUG ErrorLevel = "Debug"
|
|
ErrorINFO ErrorLevel = "Info"
|
|
ErrorWARN ErrorLevel = "Warn"
|
|
ErrorERROR ErrorLevel = "Error"
|
|
ErrorFATAL ErrorLevel = "Fatal"
|
|
ErrorPANIC ErrorLevel = "Panic"
|
|
)
|
|
|
|
// ErrorPageFunc is a function that returns an ErrorPage with the specified HTTP Status code
|
|
// This will be called by the server when it needs to render an error page
|
|
type ErrorPageFunc func(error HWSError) (ErrorPage, error)
|
|
|
|
// ErrorPage must implement a Render() function that takes in a context and ResponseWriter,
|
|
// and should write a reponse as output to the ResponseWriter.
|
|
// Server.ThrowError will call the Render() function on the current request
|
|
type ErrorPage interface {
|
|
Render(ctx context.Context, w io.Writer) error
|
|
}
|
|
|
|
// AddErrorPage registers a handler that returns an ErrorPage
|
|
func (server *Server) AddErrorPage(pageFunc ErrorPageFunc) error {
|
|
rr := httptest.NewRecorder()
|
|
req := httptest.NewRequest("GET", "/", nil)
|
|
page, err := pageFunc(HWSError{StatusCode: http.StatusInternalServerError})
|
|
if err != nil {
|
|
return errors.Wrap(err, "An error occured when trying to get the error page")
|
|
}
|
|
err = page.Render(req.Context(), rr)
|
|
if err != nil {
|
|
return errors.Wrap(err, "An error occured when trying to render the error page")
|
|
}
|
|
if len(rr.Header()) == 0 && rr.Body.String() == "" {
|
|
return errors.New("Render method of the error page did not write anything to the response writer")
|
|
}
|
|
|
|
server.errorPage = pageFunc
|
|
return nil
|
|
}
|
|
|
|
// ThrowError will write the HTTP status code to the response headers, and log
|
|
// the error with the level specified by the HWSError.
|
|
// If HWSError.RenderErrorPage is true, the error page will be rendered to the ResponseWriter
|
|
// and the request chain should be terminated.
|
|
func (server *Server) ThrowError(w http.ResponseWriter, r *http.Request, error HWSError) error {
|
|
if error.StatusCode <= 0 {
|
|
return errors.New("HWSError.StatusCode cannot be 0.")
|
|
}
|
|
if error.Message == "" {
|
|
return errors.New("HWSError.Message cannot be empty")
|
|
}
|
|
if error.Error == nil {
|
|
return errors.New("HWSError.Error cannot be nil")
|
|
}
|
|
if r == nil {
|
|
return errors.New("Request cannot be nil")
|
|
}
|
|
if !server.IsReady() {
|
|
return errors.New("ThrowError called before server started")
|
|
}
|
|
w.WriteHeader(error.StatusCode)
|
|
server.LogError(error)
|
|
if server.errorPage == nil {
|
|
server.LogError(HWSError{Message: "No error page provided", Error: nil, Level: ErrorDEBUG})
|
|
return nil
|
|
}
|
|
if error.RenderErrorPage {
|
|
server.LogError(HWSError{Message: "Error page rendering", Error: nil, Level: ErrorDEBUG})
|
|
errPage, err := server.errorPage(error)
|
|
if err != nil {
|
|
server.LogError(HWSError{Message: "Failed to get a valid error page", Error: err})
|
|
}
|
|
err = errPage.Render(r.Context(), w)
|
|
if err != nil {
|
|
server.LogError(HWSError{Message: "Failed to render error page", Error: err})
|
|
}
|
|
} else {
|
|
server.LogError(HWSError{Message: "Error page specified not to render", Error: nil, Level: ErrorDEBUG})
|
|
}
|
|
return nil
|
|
}
|
|
|
|
func (server *Server) ThrowFatal(w http.ResponseWriter, err error) {
|
|
w.WriteHeader(http.StatusInternalServerError)
|
|
server.LogFatal(err)
|
|
}
|