package web import ( "context" "log" "net/http" "os" "syscall" "time" "github.com/dimfeld/httptreemux" ) // ctxKey represents the type of value for the context key. type ctxKey int // KeyValues is how request values or stored/retrieved. const KeyValues ctxKey = 1 // Values represent state for each request. type Values struct { Now time.Time StatusCode int } // A Handler is a type that handles an http request within our own little mini // framework. type Handler func(ctx context.Context, w http.ResponseWriter, r *http.Request, params map[string]string) error // App is the entrypoint into our application and what configures our context // object for each of our http handlers. Feel free to add any configuration // data/logic on this App struct type App struct { *httptreemux.TreeMux shutdown chan os.Signal log *log.Logger mw []Middleware } // NewApp creates an App value that handle a set of routes for the application. func NewApp(shutdown chan os.Signal, log *log.Logger, mw ...Middleware) *App { app := App{ TreeMux: httptreemux.New(), shutdown: shutdown, log: log, mw: mw, } return &app } // SignalShutdown is used to gracefully shutdown the app when an integrity // issue is identified. func (a *App) SignalShutdown() { a.log.Println("error returned from handler indicated integrity issue, shutting down service") a.shutdown <- syscall.SIGSTOP } // Handle is our mechanism for mounting Handlers for a given HTTP verb and path // pair, this makes for really easy, convenient routing. func (a *App) Handle(verb, path string, handler Handler, mw ...Middleware) { // First wrap handler specific middleware around this handler. handler = wrapMiddleware(mw, handler) // Add the application's general middleware to the handler chain. handler = wrapMiddleware(a.mw, handler) // The function to execute for each request. h := func(w http.ResponseWriter, r *http.Request, params map[string]string) { // Set the context with the required values to // process the request. v := Values{ Now: time.Now(), } ctx := context.WithValue(r.Context(), KeyValues, &v) // Call the wrapped handler functions. if err := handler(ctx, w, r, params); err != nil { a.log.Printf("*****> critical shutdown error: %v", err) a.SignalShutdown() return } } // Add this handler for the specified verb and route. a.TreeMux.Handle(verb, path, h) }