package http import ( "fmt" "net/http" "os" "runtime/debug" "sync" platform "github.com/influxdata/influxdb" influxlogger "github.com/influxdata/influxdb/logger" "github.com/julienschmidt/httprouter" "go.uber.org/zap" ) // NewRouter returns a new router with a 404 handler, a 405 handler, and a panic handler. func NewRouter() *httprouter.Router { router := httprouter.New() router.NotFound = http.HandlerFunc(notFoundHandler) router.MethodNotAllowed = http.HandlerFunc(methodNotAllowedHandler) router.PanicHandler = panicHandler return router } // notFoundHandler represents a 404 handler that return a JSON response. func notFoundHandler(w http.ResponseWriter, r *http.Request) { ctx := r.Context() pe := &platform.Error{ Code: platform.ENotFound, Msg: "path not found", } EncodeError(ctx, pe, w) } // methodNotAllowedHandler represents a 405 handler that return a JSON response. func methodNotAllowedHandler(w http.ResponseWriter, r *http.Request) { ctx := r.Context() allow := w.Header().Get("Allow") pe := &platform.Error{ Code: platform.EMethodNotAllowed, Msg: fmt.Sprintf("allow: %s", allow), } EncodeError(ctx, pe, w) } // panicHandler handles panics recovered from http handlers. // It returns a json response with http status code 500 and the recovered error message. func panicHandler(w http.ResponseWriter, r *http.Request, rcv interface{}) { ctx := r.Context() pe := &platform.Error{ Code: platform.EInternal, Msg: "a panic has occurred", Err: fmt.Errorf("%v", rcv), } l := getPanicLogger() l.Error( pe.Msg, zap.String("err", pe.Err.Error()), zap.String("stack", fmt.Sprintf("%s", debug.Stack())), ) EncodeError(ctx, pe, w) } var panicLogger *zap.Logger var panicLoggerOnce sync.Once // getPanicLogger returns a logger for panicHandler. func getPanicLogger() *zap.Logger { panicLoggerOnce.Do(func() { panicLogger = influxlogger.New(os.Stderr) panicLogger = panicLogger.With(zap.String("handler", "panic")) }) return panicLogger }