Skip to content
Branch: master
Find file Copy path
Find file Copy path
3 contributors

Users who have contributed to this file

@ardan-bkennedy @jcbwlkr @superwhykz
116 lines (96 sloc) 3.4 KB
package web
import (
// 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 {
TraceID string
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 {
och *ochttp.Handler
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,
// Create an OpenCensus HTTP Handler which wraps the router. This will start
// the initial span and annotate it with information about the request/response.
// This is configured to use the W3C TraceContext standard to set the remote
// parent if an client request includes the appropriate headers.
app.och = &ochttp.Handler{
Handler: app.TreeMux,
Propagation: &tracecontext.HTTPFormat{},
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(, handler)
// The function to execute for each request.
h := func(w http.ResponseWriter, r *http.Request, params map[string]string) {
ctx, span := trace.StartSpan(r.Context(), "internal.platform.web")
defer span.End()
// Set the context with the required values to
// process the request.
v := Values{
TraceID: span.SpanContext().TraceID.String(),
Now: time.Now(),
ctx = context.WithValue(ctx, KeyValues, &v)
// Call the wrapped handler functions.
if err := handler(ctx, w, r, params); err != nil {
a.log.Printf("*****> critical shutdown error: %v", err)
// Add this handler for the specified verb and route.
a.TreeMux.Handle(verb, path, h)
// ServeHTTP implements the http.Handler interface. It overrides the ServeHTTP
// of the embedded TreeMux by using the ochttp.Handler instead. That Handler
// wraps the TreeMux handler so the routes are served.
func (a *App) ServeHTTP(w http.ResponseWriter, r *http.Request) {
a.och.ServeHTTP(w, r)
You can’t perform that action at this time.