Skip to content

Instantly share code, notes, and snippets.

@stroborobo
Created September 4, 2015 22:38
Show Gist options
  • Save stroborobo/3fa97a3151cd99ca5fd4 to your computer and use it in GitHub Desktop.
Save stroborobo/3fa97a3151cd99ca5fd4 to your computer and use it in GitHub Desktop.
package handlers
import (
"fmt"
"net/http"
"runtime"
)
// HandlerFunc is the function signature for use in Handler
type HandlerFunc func(c *Context, wr http.ResponseWriter, req *http.Request) (string, error)
// Handler encapsulates our handlers so we can pass a type save context and
// still conform to the http.Handler interface
type Handler struct {
*Context
Func HandlerFunc
}
// ServeHTTP gets the request context, calls the actual handler func and
// handles it's returned error, if any
func (h Handler) ServeHTTP(wr http.ResponseWriter, req *http.Request) {
c := RequestContext(h.Context, wr, req)
// catch handler panics
defer func() {
r := recover()
if r == nil {
return
}
// stack (copied from net/http/server.go:1284)
const size = 64 << 10
buf := make([]byte, size)
buf = buf[:runtime.Stack(buf, false)]
c.Logger.Errorf("panic recovered: %v\n%s", r, buf)
c.Metrics.Panics.Inc()
http.Error(c.Wr, errInternalServerError.Error(), http.StatusInternalServerError)
}()
// run the actual handler
tpl, origErr := h.Func(c, wr, req)
// if there's no error, write 200 OK and display the template if any
if origErr == nil {
wr.WriteHeader(200)
if tpl != "" {
h.handleErr(c, c.View.Display(wr, tpl), false)
}
return
}
// otherwise handle that motherfucker
de := NewDebugError(origErr)
err := de.Underlying()
if tpl == "" {
wr.Header().Set("Content-Type", "text/plain; charset=utf-8")
}
switch e := err.(type) {
case Redirector:
http.Redirect(wr, req, e.Path(), e.Status())
return
case *UserError:
wr.WriteHeader(e.Status())
if tpl == "" {
fmt.Fprint(wr, e)
} else {
c.View.AddError(e)
}
case Error:
c.Logger.Errorf("Status: %d - %v", e.Status(), de)
wr.WriteHeader(e.Status())
if tpl == "" {
fmt.Fprint(wr, e)
} else {
c.View.AddError(e)
}
default:
wr.WriteHeader(http.StatusInternalServerError)
h.handleErr(c, de, tpl != "")
}
if tpl == "" {
return
}
h.handleErr(c, c.View.Display(wr, tpl), false)
}
func (h Handler) handleErr(c *Context, err error, inView bool) {
if err == nil {
return
}
err = NewDebugError(err)
if inView {
c.Logger.Error(err)
if c.Debug {
c.View.AddError(err)
} else {
c.View.AddError(errInternalServerError)
}
} else {
c.Logger.Error(err)
http.Error(c.Wr, errInternalServerError.Error(), http.StatusInternalServerError)
}
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment