The other day, I posted a chunk of code called Switchboard, which was all about using Nodejitsu's node-http-proxy. I turns out there's a much easier way. HttpProxy.createServer will create an empty routing proxy, and when you ask for a backend handler that isn't present, node-http-proxy will automagically build one using internal defaults.

So, really, your code looks like this:

backends = [
    {
        name: 'Event Hub'
        host: 'http://127.0.0.1:8024'
        paths: ['^/socket.io/']
    },
    {
        name: 'Django Server'
        host: 'http://127.0.0.1:8022'
        paths: ['^/api/']
    }
]

standard_options = (t) ->
    target:
        host: t.hostname
        port: t.port
        https: (t.protocol == 'https:')
    enable:
        xforward: true

for backend in backends
    backend.paths = (new RegExp(i) for i in backend.paths)
    backend.options = standard_options(url.parse(backend.host))

find_backend = (req) ->
    pathname = url.parse(req.url).pathname
    for backend in backends
        for path in backend.paths
            m = path.exec(pathname)
            if m
                return backend
    null

proxyserver = httpProxy.createServer (req, res, proxy) ->
    backend = find_backend(req)
    if backend
        proxy.proxyRequest(req, res, backend.options)
        return @

    # Clean up if no backend found
    try
        res.writeHead 404
        res.end()
    catch error
        console.error("res.writeHead/res.end error: %s", error.message)
    undefined

proxyserver.listen(80)

Instead of all that silliness in the original, here I define my routes in a fairly standard human-readable format, and then translate them into the target style preferred by node-http-proxy. I have a simple function for looking up the back-end depending upon URL components and returning the first match. I let node-http-proxy do all the work of creating a connection to the back-end if it doesn't work.

All that crud from last week reduced to a couple of list comprehensions and translations. Sad, isn't it?

I should add that there's a bug in the current version of node-http-proxy that will cause the code above to fail. Node-http-proxy has an internal call, _getKey(), to look up that host to which it will route a request, that it should use, but does not, for the ad-hoc proxy creation. Until that bug is fixed (and yes, I have a patch submitted in), you'll have to patch the server by hand.

There is some additional magic not shown in this example.  The rewriters is a trivial add-in, and neither example handles the case where a back-end issues an http:// re-direct, but that has to be mangled to read https:// before being forwarded to the client. Again, there's a bug in the original that prevents this from working right, and I have a patch in for that as well.

Finally, the code as checked out of npm has a bug that prevents websocket upgrades from working properly. My work-around was to disable that in the code and do it myself, but that's not a satisfactory hack and I haven't sent that forward to the nodejitsu team yet.

I will say that with all of these fixes in place, I have a working solution for replacing Nginx. It proxies nicely, it proxies websockets nicely, and it proxies large-file uploads without using hideously huge memory or file buffers on the proxy server, all through SSL. We will be evaluating its performance, stability, and cross-browser issues over the next couple of weeks to ensure it's a good solution.

I will also say that reading the Nginx source code was an eye-opening experience. The core and http modules are very well-written, clear and concise, and learning from their comments and code examples has made a big difference in how I'm approaching this proxying issue.

In theory, I could reduce Switchboard down to my routines for defining backends and frontends, add the rewriter and ad-hoc websocket upgrader.  Maybe I will.  It'll be a heck of a lot shorter than the current edition.