HTTP Request Context in Golang

7 January 2015

The Go HTTP stack offers a very efficient web server implementation. As the Request object passed through to Handlers does not support memory resident storage itself, context specific to the lifetime of each request needs to be managed outside net/http.

Go web frameworks such as Goji, Beego, and Revel have built-in support for request context. Further, the Gorilla context package provides support for global request variables, though it relies on mutex to guard access to request state. If you are running directly on net/http, you can also inject a context object at the beginning of the request lifecycle which flows through the handler chain. By passing context through, rather than relying a global context object, state specific to each request can be managed without introducing locks.

type Handler func(http.ResponseWriter, *http.Request, *Context)

func (h Handler) ServeHTTP(w http.ResponseWriter, r *http.Request) {
    ctx := &Context{
        Config: Config,
        DB:     DB,
        Items:  map[string]interface{}{},
    }
    h(w, r, ctx)
}

To build a chain of Handlers which accept context, the creation of the custom Handler type above can be wrapped. The outer Handler returned will contain a reference to the Handler chain passed in. Upon execution by the networking stack, the outer Handler's ServeHTTP method will be invoked, creating the custom Context object before forwarding to the entire handler chain.

func Csrf(h Handler) Handler {
    return Handler(
        func(w http.ResponseWriter, r *http.Request, ctx *Context) {

            // Your super secure CSRF logic here...

            ctx.Items["csrf"] = token
            h(w, r, ctx)
        })
}

As Handler implements the ServeHTTP method, the first handler instance in the chain can be registered with a given pattern on your router.

func home(w http.ResponseWriter, r *http.Request, ctx *Context) {
    Render("home.tmpl", ctx.Items["csrf"])
}
router.Handle("/", Csrf(Handler(home)))

At runtime, the Csrf handler executes first, calling ServeHTTP to setup the request environment. Next, the Csrf handler itself is called which sets a token within the Context. Finally, the home Handler is invoked wherein request context state is made available.

By Aaron Dunnington