Oct 14, 2013

Some time ago I released
nosurf,
a Go middleware for mitigating
Cross-Site Request Forgery attacks.
Writing a seemingly simple and small package
was enough to fall in love
with how Go handles HTTP.
Yet, it's up to us to either
embrace the standard HTTP facilities
or fragmentate, sacrificing composability and modularity.

http.Handler is THE interface

Unified HTTP interfaces for
web apps written in certain programming languages, like
WSGI for Python
and Rack for Ruby
are a great idea, but they weren't always there.
For instance, Rack only emerged in 2007,
when Rails had already been
going strong
for a while.

Meanwhile in Go, the only interface needed
has been in development since 2009,
and although it's been through some serious
changes since that, by the end of 2011,
months before Go 1.0 was released,
it had already stabilized.

Of course, I'm talking about the mighty http.Handler.

typeHandlerinterface{ServeHTTP(ResponseWriter,*Request)}

To be able to handle HTTP requests,
your type only needs to implement this one method.
The method reads the request info from the given *Request
and writes a response into the given ResponseWriter.
Seems simple enough, right?

Complement, don't replace

Yet, when building abstractions on top of that, some get it wrong.
Take for example
Mango,
described by its author as
"a modular web-application framework for Go,
inspired by Rack and PEP333".

Looks simple, concise and very similar to WSGI or Rack, right?
Except for one thing. While with dynamic/duck typing,
you could have any iterable for a body,
here mango.Body is simply a string.
Essentially, that takes away the ability to do
any sort of streaming responses with Mango.
Even if it were to expose a ResponseWriter,
anything written to it would clash with the returned values,
since they're only returned at the end of the function,
after the calls to ResponseWriter have already been made.

That's bad. Whether you need another interface
on top of existing net/http is a matter of taste,
but even if you do, it should not take functionality away.
An interface that is nicer to code with,
but takes away important functions is clearly inferior.

The right way

A popular "micro" web framework web.go
deals with this in a simple, yet much better way.
Its handlers take a pointer to web.Context
as an optional first argument.

web.Context does not take the standard HTTP handler structures away.
Instead, the *Request argument is available as a struct member
and Contextimplements the required ResponseWriter methods itself
embeds the original ResponseWriter.
The string you return from the function (if any) is simply appended
to the response.

That is a good design choice and I think it goes well with Go's philosophy.
Even though you get a nice higher-level API,
you don't have to sacrifice the low-level control over the request handling.

Start now

Go's HTTP library infrastructure, despite growing rapidly,
still has some gaps left to fill.
But the last thing we need is fragmentation and
annoying incompatibilities due to poor design
and abstractions that actually take important functionality away.
Embracing and supporting the standard Go HTTP facilities
is, in my humble opinion, the straightest way to
having functional and modular 3rd-party HTTP tools.