grunt-connect-proxy = function (grunt) { var proxySnippet = require("./lib/utils.js").proxyRequest; // Project configuration. grunt.initConfig({ jshint: { all: [ 'Gruntfile.js', 'tasks/*.js', 'lib/*.js', '<%= nodeunit.tests %>' ], options: { jshintrc: '.jshintrc' } }, // Before generating any new files, remove any previously-created files. clean: { tests: ['tmp'] }, // Configuration to be run (and then tested). connect: { options: { port: 9000, // change this to '0.0.0.0' to access the server from outside hostname: 'localhost' }, proxies: [ { context: '/defaults', host: 'www.defaults.com' }, { context: '/full', host: 'www.full.com', port: 8080, https: true, xforward: true, rewrite: { '^/full': '/anothercontext' }, headers: { "X-Proxied-Header": "added" }, ws: true }, { context: '/context/test', host: 'www.anothercontext.com', rewrite: { '^/context': '/anothercontext', 'test': 'testing' } }, { context: '/invalidrewrite', host: 'www.invalidrewrite.com', rewrite: { '^/undefined': undefined, '^/notstring': 13, '^/in': '/thisis' } }, { context: '/missinghost' }, { host: 'www.missingcontext.com' }, { context: ['/array1','/array2'], host: 'www.defaults.com' } ], server2: { proxies: [ { context: '/', host: 'www.server2.com' } ] }, server3: { appendProxies: false, proxies: [ { context: '/server3', host: 'www.server3.com', port: 8080, } ] }, request: { options: { middleware: function (connect, options) { return [require('./lib/utils').proxyRequest]; } }, proxies: [ { context: '/request', host: 'localhost', port: 8080, headers: { "x-proxied-header": "added" } }, { context: '/hideHeaders', host: 'localhost', port: 8081, hideHeaders: ['x-hidden-header-1', 'x-hidden-header-2'] } ] } }, // Unit tests. nodeunit: { tests: 'test/connect_proxy_test.js', server2: 'test/server2_proxy_test.js', server3: 'test/server3_proxy_test.js', utils: 'test/utils_test.js', request: 'test/request_test.js', hideHeaders: 'test/hide_headers_test.js' } }); // Actually load this plugin's task(s). grunt.loadTasks('tasks'); // These plugins provide necessary tasks. grunt.loadNpmTasks('grunt-contrib-connect'); grunt.loadNpmTasks('grunt-contrib-jshint'); grunt.loadNpmTasks('grunt-contrib-clean'); grunt.loadNpmTasks('grunt-contrib-nodeunit'); // Whenever the "test" task is run, first clean the "tmp" dir, then run this // plugin's task(s), then test the result. grunt.registerTask('test', [ 'clean', 'nodeunit:utils', 'configureProxies', 'nodeunit:tests', 'configureProxies:server2', 'nodeunit:server2', 'configureProxies:server3', 'nodeunit:server3', 'configureProxies:request', 'connect:request', 'nodeunit:request', 'nodeunit:hideHeaders' ]); // specifically test that option inheritance works for multi-level config grunt.registerTask('test-inheritance', [ 'clean', 'configureProxies:serv ...
n/a
matchContext = function (context, url) { var positiveContexts, negativeContexts, positiveMatch, negativeMatch; var contexts = context; if (!_.isArray(contexts)) { contexts = [contexts]; } positiveContexts = _.filter(contexts, function(c){return c.charAt(0) !== '!';}); negativeContexts = _.filter(contexts, function(c){return c.charAt(0) === '!';}); // Remove the '!' character from the contexts negativeContexts = _.map(negativeContexts, function(c){return c.slice(1);}); negativeMatch = _.find(negativeContexts, function(c){return url.lastIndexOf(c, 0) === 0;}); // If any context negates this url, it must not be proxied. if (negativeMatch) { return false; } positiveMatch = _.find(positiveContexts, function(c){return url.lastIndexOf(c, 0) === 0;}); // If there is any positive match, lets proxy this url. return positiveMatch != null; }
...
return positiveMatch != null;
};
function onUpgrade(req, socket, head) {
var proxied = false;
proxies.forEach(function(proxy) {
if (!proxied && req && proxy.config.ws && utils.matchContext
(proxy.config.context, req.url)) {
if (proxy.config.rules.length) {
proxy.config.rules.forEach(rewrite(req));
}
proxy.server.ws(req, socket, head);
proxied = true;
...
processRewrites = function (rewrites) { var rules = []; Object.keys(rewrites || {}).forEach(function (from) { var rule = { from: from, to: rewrites[from] }; if (utils.validateRewrite(rule)) { rule.from = new RegExp(rule.from); rules.push(rule); grunt.log.writeln('Rewrite rule created for: [' + rule.from + ' -> ' + rule.to + '].'); } else { grunt.log.error('Invalid rule'); } }); return rules; }
...
port: 80,
https: false,
xforward: false,
rules: [],
ws: false
});
if (validateProxyConfig(proxyOption)) {
proxyOption.rules = utils.processRewrites(proxyOption.rewrite);
utils.registerProxy({
server: httpProxy.createProxyServer({
target: proxyOption,
secure: proxyOption.https,
xfwd: proxyOption.xforward
}).on('error', function (err, req, res) {
grunt.log.error('Proxy error: ', err.code);
...
proxies = function () { return proxies; }
n/a
proxyRequest = function (req, res, next) { var proxied = false; enableWebsocket(req.connection.server); proxies.forEach(function(proxy) { if (!proxied && req && utils.matchContext(proxy.config.context, req.url)) { if (proxy.config.rules.length) { proxy.config.rules.forEach(rewrite(req)); } // Add headers present in the config object if (proxy.config.headers != null) { _.forOwn(proxy.config.headers, function(value, key) { req.headers[key] = value; }); } proxy.server.proxyRequest(req, res, proxy.server); removeHiddenHeaders(proxy); // proxying twice would cause the writing to a response header that is already sent. Bad config! proxied = true; var source = req.originalUrl; var target = (proxy.server.options.secure ? 'https://' : 'http://') + proxy.server.options.target.host + ':' + proxy .server.options.target.port + req.url; grunt.log.verbose.writeln('Proxied request: ' + source + ' -> ' + target + '\n' + JSON.stringify(req.headers, true, 2)); } }); if (!proxied) { next(); } }
...
// Add headers present in the config object
if (proxy.config.headers != null) {
_.forOwn(proxy.config.headers, function(value, key) {
req.headers[key] = value;
});
}
proxy.server.proxyRequest(req, res, proxy.server);
removeHiddenHeaders(proxy);
// proxying twice would cause the writing to a response header that is already sent. Bad config!
proxied = true;
var source = req.originalUrl;
var target = (proxy.server.options.secure ? 'https://' : 'http://') + proxy.server.options.target.host + '
;:' + proxy.server.options.target.port + req.url;
...
registerProxy = function (proxy) { proxies.push(proxy); }
...
https: false,
xforward: false,
rules: [],
ws: false
});
if (validateProxyConfig(proxyOption)) {
proxyOption.rules = utils.processRewrites(proxyOption.rewrite);
utils.registerProxy({
server: httpProxy.createProxyServer({
target: proxyOption,
secure: proxyOption.https,
xfwd: proxyOption.xforward
}).on('error', function (err, req, res) {
grunt.log.error('Proxy error: ', err.code);
}),
...
reset = function () { proxies = []; }
...
}
if (proxyOption.https && proxyOption.port === 80) {
grunt.log.warn('Proxy for ' + proxyOption.context + ' is using https on port 80. Are you sure this is correct
?');
}
return true;
};
utils.reset();
utils.log = grunt.log;
if (config) {
var connectOptions = grunt.config('connect.'+config) || [];
if (typeof connectOptions.appendProxies === 'undefined' || connectOptions.appendProxies) {
proxyOptions = proxyOptions.concat(grunt.config('connect.proxies') || []);
}
proxyOptions = proxyOptions.concat(connectOptions.proxies || []);
...
validateRewrite = function (rule) { if (!rule || typeof rule.from === 'undefined' || typeof rule.to === 'undefined' || typeof rule.from !== 'string' || typeof rule.to !== 'string') { return false; } return true; }
...
Object.keys(rewrites || {}).forEach(function (from) {
var rule = {
from: from,
to: rewrites[from]
};
if (utils.validateRewrite(rule)) {
rule.from = new RegExp(rule.from);
rules.push(rule);
grunt.log.writeln('Rewrite rule created for: [' + rule.from + ' -> ' + rule.to + '].');
} else {
grunt.log.error('Invalid rule');
}
});
...