From 9a123c9281506eb3844522ab0574d555a62b651e Mon Sep 17 00:00:00 2001 From: Alexandre Tuleu Date: Wed, 12 Aug 2015 19:23:53 +0200 Subject: [PATCH] Adds error formatting --- error_formatter.go | 42 ++++++++++++++++++++++++++++++++++++++++++ recovery.go | 29 +++++++++++------------------ static.go | 7 ++++++- 3 files changed, 59 insertions(+), 19 deletions(-) create mode 100644 error_formatter.go diff --git a/error_formatter.go b/error_formatter.go new file mode 100644 index 0000000..7a17377 --- /dev/null +++ b/error_formatter.go @@ -0,0 +1,42 @@ +package narco + +import ( + "fmt" + "net/http" + + "golang.org/x/net/context" +) + +type narcoKey int + +const errorFormatterKey narcoKey = 1 + +type ErrorFomatter func(ctx context.Context, rw http.ResponseWriter, message interface{}, status int) + +func Error(ctx context.Context, rw http.ResponseWriter, message interface{}, status int) { + fmter, ok := ctx.Value(errorFormatterKey).(ErrorFomatter) + if ok == false { + //default to http formatter + http.Error(rw, fmt.Sprintf("%s", message), status) + return + } + fmter(ctx, rw, message, status) +} + +var narcoDefaultFormatter ErrorFomatter + +func TextErrorFormatter(ctx context.Context, rw http.ResponseWriter, message interface{}, status int) { + rw.Header()["Content-Type"] = []string{"text/plain; charset=utf-8"} + rw.WriteHeader(status) + fmt.Fprintf(rw, "%s", message) +} + +func BasicHTMLErrorFormatter(ctx context.Context, rw http.ResponseWriter, message interface{}, status int) { + rw.Header()["Content-Type"] = []string{"text/html; charset=utf-8"} + rw.WriteHeader(status) + fmt.Fprintf(rw, `

Error %d

%s`, status, message) +} + +func WithErrorFormatter(ctx context.Context, fmter ErrorFomatter) context.Context { + return context.WithValue(ctx, errorFormatterKey, fmter) +} diff --git a/recovery.go b/recovery.go index 9debe98..fb3d99c 100644 --- a/recovery.go +++ b/recovery.go @@ -2,7 +2,6 @@ package narco import ( "fmt" - "io" "log" "net/http" "os" @@ -12,19 +11,14 @@ import ( "golang.org/x/net/context" ) -type StackResponseFormatter func(w io.Writer, err interface{}, stack []byte) +type StackResponseFormatter func(err interface{}, stack []byte) string -func GoFormatStack(w io.Writer, err interface{}, stack []byte) { - fmt.Fprintf(w, "Server internal error: %s\n%s", err, stack) +func GoFormatStack(err interface{}, stack []byte) string { + return fmt.Sprintf("Server internal error: %s\n%s", err, stack) } -func HtmlFormatStack(w io.Writer, err interface{}, stack []byte) { - fmt.Fprintf(w, ` - -

Server internal error: %s

-
%s
- -`, err, stack) +func HtmlFormatStack(err interface{}, stack []byte) string { + return fmt.Sprintf(`

Server internal error: %s

%s
`, err, stack) } type Recoverer struct { @@ -43,27 +37,26 @@ func NewRecoverer() *Recoverer { func (rec *Recoverer) Wrap() func(chain.Handler) chain.Handler { return func(other chain.Handler) chain.Handler { - return chain.HandlerFunc(func(ctx context.Context, h http.ResponseWriter, req *http.Request) { + return chain.HandlerFunc(func(ctx context.Context, rw http.ResponseWriter, req *http.Request) { defer func() { err := recover() if err == nil { return } - // h.Header()["Content-Type"] = []string{"text/html; charset=utf-8"} - h.WriteHeader(http.StatusInternalServerError) - stack := make([]byte, 8*1024) stack = stack[:runtime.Stack(stack, rec.StackOtherGoroutine)] rec.Logger.Printf("PANIC: %s\n%s", err, stack) + var message string if rec.ShowStackInResponse != nil { - rec.ShowStackInResponse(h, err, stack) + message = rec.ShowStackInResponse(err, stack) } else { - fmt.Fprintf(h, "%s\n", http.StatusText(http.StatusInternalServerError)) + message = http.StatusText(http.StatusInternalServerError) } + Error(ctx, rw, message, http.StatusInternalServerError) }() - other.ServeHTTPContext(ctx, h, req) + other.ServeHTTPContext(ctx, rw, req) }) } } diff --git a/static.go b/static.go index 63fdffc..1f30110 100644 --- a/static.go +++ b/static.go @@ -88,7 +88,12 @@ func (fs *StaticFileServer) ServeHTTP(rw http.ResponseWriter, req *http.Request) //chain handler interface func (fs *StaticFileServer) ServeHTTPContext(ctx context.Context, rw http.ResponseWriter, req *http.Request) { - fs.ServeHTTP(rw, req) + err := fs.ServeHTTPError(rw, req) + if err != nil { + fsErr := err.(StaticFileServerError) + //use our own context aware error handler + Error(ctx, rw, err.Error(), fsErr.status) + } } // chain wrapper. we just ignore the error, and send the request to