From 329bbb7cb873d32b54c227b52c89e95c8bfdfd50 Mon Sep 17 00:00:00 2001 From: youngjun827 Date: Tue, 7 Nov 2023 17:38:58 -0500 Subject: [PATCH] refactor(web): Improve error handling and shutdown logic in web framework --- business/web/v1/response/response.go | 34 ++++++++++++++++++++++++++++ foundation/web/shutdown.go | 26 +++++++++++++++++++++ foundation/web/web.go | 25 +++++++++++++++++++- 3 files changed, 84 insertions(+), 1 deletion(-) create mode 100644 business/web/v1/response/response.go create mode 100644 foundation/web/shutdown.go diff --git a/business/web/v1/response/response.go b/business/web/v1/response/response.go new file mode 100644 index 0000000..43508b4 --- /dev/null +++ b/business/web/v1/response/response.go @@ -0,0 +1,34 @@ +package response + +import "errors" + +type ErrorDocument struct { + Error string `json:"error"` + Fields map[string]string `json:"fields,omitempty"` +} + +type Error struct { + Err error + Status int +} + +func NewError(err error, status int) error { + return &Error{err, status} +} + +func (re *Error) Error() string { + return re.Err.Error() +} + +func IsError(err error) bool { + var re *Error + return errors.As(err, &re) +} + +func GetError(err error) *Error { + var re *Error + if !errors.As(err, &re) { + return nil + } + return re +} \ No newline at end of file diff --git a/foundation/web/shutdown.go b/foundation/web/shutdown.go new file mode 100644 index 0000000..120e6c7 --- /dev/null +++ b/foundation/web/shutdown.go @@ -0,0 +1,26 @@ +package web + +import "errors" + +// shutdownError is a type used to help with the graceful termination of the service. +type shutdownError struct { + Message string +} + +// NewShutdownError returns an error that causes the framework to signal +// a graceful shutdown. +func NewShutdownError(message string) error { + return &shutdownError{message} +} + +// Error is the implementation of the error interface. +func (se *shutdownError) Error() string { + return se.Message +} + +// IsShutdown checks to see if the shutdown error is contained +// in the specified error value. +func IsShutdown(err error) bool { + var se *shutdownError + return errors.As(err, &se) +} \ No newline at end of file diff --git a/foundation/web/web.go b/foundation/web/web.go index e0b8fd2..c8ff39e 100644 --- a/foundation/web/web.go +++ b/foundation/web/web.go @@ -3,8 +3,10 @@ package web import ( "context" + "errors" "net/http" "os" + "syscall" "time" "github.com/go-chi/chi/v5" @@ -28,6 +30,22 @@ func NewApp(shutdown chan os.Signal, mw ...Middleware) *App { } } +func (a *App) SignalShutdown() { + a.shutdown <- syscall.SIGTERM +} + +func validateShutdown(err error) bool { + switch { + case errors.Is(err, syscall.EPIPE): + return false + + case errors.Is(err, syscall.ECONNRESET): + return false + } + + return true +} + func HandlerAdapter(handler Handler) http.HandlerFunc { return func(w http.ResponseWriter, r *http.Request) { err := handler(r.Context(), w, r) @@ -50,10 +68,15 @@ func (a *App) Handle(method string, path string, handler Handler, mw ...Middlewa ctx = SetValues(ctx, &v) err := wrappedHandler(ctx, w, r) if err != nil { - http.Error(w, "Internal Server Error", http.StatusInternalServerError) + if validateShutdown(err) { + a.SignalShutdown() + return err + } } return nil } a.Mux.MethodFunc(method, path, HandlerAdapter(customHandler)) } + +