Meta trips

Go middleware stack in 13 LOC


Perfection is Achieved Not When There Is Nothing More to Add, But When There Is Nothing Left to Take Away.

Antoine de Saint-Exupery

Today I proudly present my micro framework for middleware in Go. After seven totally different iterations, I finally settled with a middleware solution that is both small and powerful.

The benefit of middleware

You probably heard about middleware stacks - a concept that became really popular in the Ruby world with the advent of the rack library by Christian Neukirchen.

The idea is to combine apps and middleware that conform to a simple interface and may be stacked together in different ways.

Since Ruby has no way to enforce conformance to interfaces, it is up to the programmer to adher to the specification.

A Rack application is an Ruby object (not a class) that responds to call. It takes exactly one argument, the environment and returns an Array of exactly three values: The status, the headers, and the body.

For the middleware the spec is almost the same, just that is must be a class with an initialization method that takes as first parameter the inner app.

Here is what a rack middleware looks like:

class ContentType
  include Rack::Utils

  def initialize(app, content_type = "text/html")
    @app, @content_type = app, content_type
  end

  def call(env)
    status, headers, body = @app.call(env)
    headers = Utils::HeaderHash.new(headers)

    unless STATUS_WITH_NO_ENTITY_BODY.include?(status)
      headers['Content-Type'] ||= @content_type
    end

    return [status, headers, body]
  end
end

This middleware modifies the headers of the inner app and sets the Content-Type header if it is not present.

And here is how it can be used within an app.

# hello is our simple 'app'
hello = Proc.new {|env| [200, {}, "Hello World"]}

# composes the app and the middlewares
builder = Rack::Builder.new do

  # use the middleware
  use Rack::ContentType, "text/plain"

  # make use of other middlewares the same way via 'use'

  # run the app
  run hello
end

# let the builder serve the requests, run the webserver
Rack::Handler::Mongrel.run builder, :Port => 9292

Inside the builder block all middlewares are injected via use. And after the last middleware the app is registered with run.

Now, when a request is served, the environment is passed to the first middleware which calls the next and so on until all of them return.

What is nice about this concept, is that it allows us to develop a bunch of middlewares that may be composed in different ways to form more complex stacks.

It promotes code reuse, decoupling, and composition.

Now back to Go

So coming back to Go, we have interfaces baked into the language and the compiler ensures that they are implemented.

The canonical interface for a web application is the http.Handler interface from the net/http package.

type Handler interface {
  ServeHTTP(ResponseWriter, *Request)
}

Each Go type that has a ServeHTTP method conforming to this signature may be used to handle a http.Request.

package main

import (
  "fmt"
  "net/http"
)

type a struct{}

func (a) ServeHTTP(w http.ResponseWriter, r *http.Request) {
  fmt.Fprint(w, "hello from a")
}

func b(w http.ResponseWriter, r *http.Request) {
  fmt.Fprint(w, "hello from b")
}

func main() {
  // handle route /a with handler a{}
  http.Handle("/a", a{})

  // HandlerFunc wraps b with a type fulfilling the Handler interface
  http.Handle("/b", http.HandlerFunc(b)) 
  
  // start the server
  http.ListenAndServe(":8080", nil)
}

Now /a is served by a{} and /b is served by http.HandlerFunc(b).

We have the same nice decoupling like rack plus the compiler checking if our handler types implement the http.Handler interface.

Middlewares anywhere?

Ok, so the http.Handler correspond to the app in rack, but where is the middleware?

It turns out that since we already got the nice http.Handler interface and Go allows composition, we could simply embed the http.Handler interface within a new struct type.

That type would de facto wrap the app while also implementing the http.Handler interface.

// this is our middleware
type contentType struct {
  // the content type we want to set
  ctype string

  // here is the app or the next middleware
  next http.Handler
}

func (c *contentType) ServeHTTP(w http.ResponseWriter, r *http.Request) {
  // do your thing
  w.Header().Set("Content-Type", c.ctype)

  // run the next handler
  c.next.ServeHTTP(w, r)
}

func appFunc(w http.ResponseWriter, r *http.Request) {
  fmt.Fprint(w, "hello world")
}

// http.HandlerFunc adapts appFunc to be an http.Handler
var app = http.HandlerFunc(appFunc)

func main() {
  http.Handle("/hello", &contentType{"text/plain",app})
  http.ListenAndServe(":8080", nil)
}

Now contentType acts like a middleware by wrapping the app.

The “but”

But there is a subtle difference. While for rack middleware it is perfectly possible to check what the inner app has written to the body and decide to return something completely different, our handler middleware may not.

That is because when the ServerHTTP method is called and passes the http.ResponseWriter value to the app, the app writes to the http.ResponseWriter and those bytes are immediately sent over the wire. This means it is too late to send headers and a status code.

Let’s for now ignore this problem. We will come back to it later.

We have some other flaws. Imagine we use 10 or more middlewares. It could look like this:

http.Handle("/hello", &A{optionsA, &B{optionsB, &C{optionsC, &D{optionsD, &E{optionsE, &F{optionsF, app}}}}}})

Or slightly better:

http.Handle("/hello", &A{
  optionsA,
  &B{
    optionsB,
    &C{
      optionsC,
      &D{
        optionsD,
        &E{
          optionsE,
          &F{
            optionsF,
            app,
          },
        },
      },
    },
  },
})

Here A, B, … are different middlewares and optionA, … are the corresponding options. Now even if we pack all the options of a middleware in a single attribute (as above), stacking multiple middlewares clutters your code base and makes it hard to understand the flow or to change the order of some middlewares.

Now imagine some middleware stacks should be reused in different routes: That gets confusing very quickly unlike with rack.

Wrap to the rescue

Here is where the wrap library comes into play.

At the core of the library is just 13 lines of code, so let’s just walk through them.

type Wrapper interface {
  Wrap(next http.Handler) http.Handler
}

var noop = http.HandlerFunc(func(http.ResponseWriter, *http.Request) {})

func New(wrapper ...Wrapper) (h http.Handler) {
  h = noop
  for i := len(wrapper) -1; i >=0; i-- {
    h = wrapper[i].Wrap(h)
  }
  return
}

The key concept is the Wrapper interface. A Wrapper has a Wrap method that takes the next http.Handler in the stack and returns a http.Handler that is wrapping it. This handler is wrapped with another one and so on.

Confused? Let’s see what a typical Wrapper looks like:

type ContentType string

// Wrap wraps the next handler. It returns a handler that
// first sets the content type and then runs the next handler.
func (c ContentType) Wrap(next http.Handler) http.Handler {

  return http.HandlerFunc(
    func(wr http.ResponseWriter, req *http.Request) {
      // set the content type
      wr.Header().Set("Content-Type", string(c))
      
      // run the next handler in the stack
      next.ServeHTTP(wr, req)
    },
  )
}

Typically a wrapper will return an anonymous function that happens to be a http.Handler. Thanks to a closure, the receiver variable c and the parameter next can be accessed by the anonymous function when it is running later on. The http.HandlerFunc adapts the given function to make it implement the http.Handler interface. Wrap returns a http.Handler that will run this anonymous function when the ServeHTTP method is called.

This function first sets the content type that is contained within c and then runs the next handler of the middleware chain.

Here is a complete example (making use of the wrap library):

package main

import (
  "fmt"
  "net/http"

  "github.com/go-on/wrap"
)

type ContentType string

func (c ContentType) Wrap(next http.Handler) http.Handler {
  return http.HandlerFunc(
    func(wr http.ResponseWriter, req *http.Request) {
      wr.Header().Set("Content-Type", string(c))
      next.ServeHTTP(wr, req)
    },
  )
}

type BeforeString string

func (b BeforeString) Wrap(next http.Handler) http.Handler {
  return http.HandlerFunc(
    func(wr http.ResponseWriter, req *http.Request) {
      wr.Write([]byte(b))
      next.ServeHTTP(wr, req)
    },
  )
}

func world(w http.ResponseWriter, r *http.Request) {
  fmt.Fprint(w, "world")
}

func main() {
  stack := wrap.New(
    ContentType("text/plain"),
    BeforeString("hello "),
    wrap.HandlerFunc(world),
  )

  http.ListenAndServe(":8081", stack)
}

Here the flow of middlewares is easy to grasp: The request “runs through middleware” top-down and the app is the last handler that runs. Also the order can easily be changed, since you just have to swap lines and do not care about brackets and parentheses.

Let’s see another example:

package main

// the following is part of the wrap library, copied for demonstration
type Wrapper interface {
  Wrap(next http.Handler) http.Handler
}

var noop = http.HandlerFunc(func(http.ResponseWriter, *http.Request) {})

// creates a new stack of wrappers.
// in fact the order is reversed and the 'last' wrapper (the first parameter)
// is returned. That one wraps the previous handler and so on
func New(wrapper ...Wrapper) (h http.Handler) {
  h = noop
  for i := len(wrapper) -1; i >=0; i-- {
    h = wrapper[i].Wrap(h)
  }
  return
}
// end of wrap library

// Before implements a Wrapper that runs some handler before the next
// handler in the middleware chain
type Before struct { 
  MyWork http.Handler 
}

// Wrap implements the Wrapper interface
// The returned handler first runs b.MyWork and then the next handler.
func (b *Before) Wrap(next http.Handler) http.Handler {
  return http.HandlerFunc(
    func(wr http.ResponseWriter, req *http.Request) {
      b.MyWork.ServeHTTP(wr, req)
      next.ServeHTTP(wr, req)
    },
  )
}

// a simple app writing a string
type String string

// implements the http.Handler interface, serving the string
func (s String) ServeHTTP(w http.ResponseWriter, r *http.Request) {
  fmt.Fprint(w, s)
}

// implements the Wrapper interface by ignoring the next handler
// and returning the own handler that is serving the string
func (s String) Wrap(next http.Handler) http.Handler {
  return s
}

func main() {

  // create a new stack
  stack := New(
    // the outermost wrapper
    // its http.Handler runs the whole stack and is returned by New
    &Before{String("one, ")},

    // the wrapper in the middle
    // its http.Handler is called from above
    &Before{String("two, ")},

    // the app that is a wrapper too
    // its http.Handler is called from the middle
    String("three!"),
  )

  // serve with the stack
  http.ListenAndServe(":8080", stack)
  
  // Output:
  // one, two, three!
}

Did you see how nicely the String named type implements both the http.Handler and Wrapper interfaces? Here you can see where Go interfaces really shine.

Now lets look back at the New function. It accepts a set of values that implement the Wrapper interface. The last Wrapper to be called needs to be the last one in the list. This last Wrapper value may call the noop handler as its next handler, which does nothing and ends the chain of middleware function calls.

String returns as handler itself and its ServeHTTP method writes the string to the ResponseWriter.

At the end the first wrapper is called and returns the handler that is wrapping all the other handlers (the outermost). This handler is returned by New and when its ServerHTTP method is called and the request is served, the whole chain of closured handlers unwraps.

Back to our last problem

So while I hope you did already enjoy the beauty and simplicity of the 13 lines, we have still one problem left:

How can handlers peek into what the next handler has written?

Let’s see how the middleware does it:

// Catch is a function that is called when there was a panic
type Catch func(recovered interface{}, w http.ResponseWriter, r *http.Request)


func (c Catch) Wrap(next http.Handler) http.Handler
  return http.HandlerFunc(func (w http.ResponseWriter, r *http.Request) {
    buf := helper.NewResponseBuffer(w)
    defer func() {
      if p := recover(); p != nil {
        c(p, w, r)
      } else {
        buf.WriteAllTo(w)
      }
    }()
    next.ServeHTTP(buf, r)
  })
}

Since the http.ResponseWriter is also an interface we could replace it with a buffer that lets us examine the writes. This buffer is passed to the next handler instead of the original ResponseWriter.

The buffer used here is from the repository of blessed wrap middlewares (helper package). Have a look a this repo there is some useful stuff in it and feel free to contribute!

Stacks of stacks

Since the stack returned by wrap.New() is a http.Handler it may also be returned from a Wrapper.

So it is perfectly possible to build reusable stacks and inject them somewhere inside other stacks.

Outlook

While this post was not a good example for “less is more”, I hope, the wrap library is.

In the next weeks I will write some (shorter) posts, highlighting some middlewares, a router and other libraries I wrote as part of the emerging and modular go-on webframework.

Stay tuned!

Links

comments powered by Disqus