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, `
%s- -`, err, stack) +func HtmlFormatStack(err interface{}, stack []byte) string { + return fmt.Sprintf(`
%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