cookiesproxygrunt-connect-proxy

Rewrite cookie paths when using grunt-connect-proxy


During development I use grunt-connect-proxy to make some remote APIs locally available. This works just fine, except that the rewrite rules that I use are not applied to cookies:

proxies: [{
  context: '/publicPath',
  host: 'localhost',
  port: 8080,
  rewrite: {
    '^/publicPath': '/privatePath'
  }
}]

If the remote API sets a cookie with path /privatePath it must be rewritten to /publicPath.

E.g. When using Apache httpd I'd use the ProxyPassReverseCookiePath-Directive. How do I do it with grunt-connect-proxy?


Solution

  • Thanks to this answer in "Rewrite response headers with node-http-proxy" I managed to figure it out. I created the following middleware:

    function rewriteSetCookie(req, res, next) {
        var isProxyRequest = req.url.lastIndexOf('/publicPath', 0) === 0;
        if (isProxyRequest) {
            // we intercept the writeHead function, so that we can exchange headers just before they are written
            var oldWriteHead = res.writeHead;
            res.writeHead = function () {
                var cookie = res.getHeader('Set-Cookie');
                if (cookie) {
                    res.setHeader('Set-Cookie', cookie.map(function(item) {
                        // Replace paths in all cookies. The simple string/replace approach might be too naive in some cases, so check before you copy&paste before thinking
                        return item.replace(/\/privatePath/, '/publicPath');
                    }));
                }
                oldWriteHead.apply(res, arguments);
            };
        }
        next();
    }
    

    Just for reference here is the full configuration so that you can see how to use the middleware:

    connect: {
        server: {
            options: {
                hostname: 'localhost',
                base: 'myBaseDir',
                open: true,
                middleware: function (connect, options) {
                    if (!Array.isArray(options.base)) {
                        options.base = [options.base];
                    }
    
                    // Setup the proxy
                    var middlewares = [rewriteSetCookie, proxySnippet];
                    //                 ^^^^^^^^^^^^^^^^- Here is is used!
    
                    // Serve static files.
                    options.base.forEach(function(base) {
                        middlewares.push(connect.static(base));
                    });
    
                    // Make directory browse-able.
                    var directory = options.directory || options.base[options.base.length - 1];
                    middlewares.push(connect.directory(directory));
    
                    return middlewares;
                }
            },
            proxies: [{
                context: '/publicPath',
                host: 'localhost',
                port: 8080,
                rewrite: {
                    '^/publicPath': '/privatePath'
                }
            }]
        }
    }