|
|
var os = require('os'); var http = require('http'); var https = require('https'); var owns = {}.hasOwnProperty;
module.exports = function proxyMiddleware(options) { //enable ability to quickly pass a url for shorthand setup
if(typeof options === 'string'){ options = require('url').parse(options); }
var httpLib = options.protocol === 'https:' ? https : http; var request = httpLib.request;
options = options || {}; options.hostname = options.hostname; options.port = options.port; options.pathname = options.pathname || '/';
return function (req, resp, next) { var url = req.url; // You can pass the route within the options, as well
if (typeof options.route === 'string') { if (url === options.route) { url = ''; } else if (url.slice(0, options.route.length) === options.route) { url = url.slice(options.route.length); } else { return next(); } }
//options for this request
var opts = extend({}, options); if (url && url.charAt(0) === '?') { // prevent /api/resource/?offset=0
if (options.pathname.length > 1 && options.pathname.charAt(options.pathname.length - 1) === '/') { opts.path = options.pathname.substring(0, options.pathname.length - 1) + url; } else { opts.path = options.pathname + url; } } else if (url) { opts.path = slashJoin(options.pathname, url); } else { opts.path = options.pathname; } opts.method = req.method; opts.headers = options.headers ? merge(req.headers, options.headers) : req.headers;
applyViaHeader(req.headers, opts, opts.headers);
if (!options.preserveHost) { // Forwarding the host breaks dotcloud
delete opts.headers.host; }
var myReq = request(opts, function (myRes) { var statusCode = myRes.statusCode , headers = myRes.headers , location = headers.location; // Fix the location
if (((statusCode > 300 && statusCode < 304) || statusCode === 201) && location && location.indexOf(options.href) > -1) { // absoulte path
headers.location = location.replace(options.href, slashJoin('/', slashJoin((options.route || ''), ''))); } applyViaHeader(myRes.headers, opts, myRes.headers); rewriteCookieHosts(myRes.headers, opts, myRes.headers, req); resp.writeHead(myRes.statusCode, myRes.headers); myRes.on('error', function (err) { next(err); }); myRes.pipe(resp); }); myReq.on('error', function (err) { next(err); }); if (!req.readable) { myReq.end(); } else { req.pipe(myReq); } }; };
function applyViaHeader(existingHeaders, opts, applyTo) { if (!opts.via) return;
var viaName = (true === opts.via) ? os.hostname() : opts.via; var viaHeader = '1.1 ' + viaName; if(existingHeaders.via) { viaHeader = existingHeaders.via + ', ' + viaHeader; }
applyTo.via = viaHeader; }
function rewriteCookieHosts(existingHeaders, opts, applyTo, req) { if (!opts.cookieRewrite || !owns.call(existingHeaders, 'set-cookie')) { return; }
var existingCookies = existingHeaders['set-cookie'], rewrittenCookies = [], rewriteHostname = (true === opts.cookieRewrite) ? os.hostname() : opts.cookieRewrite;
if (!Array.isArray(existingCookies)) { existingCookies = [ existingCookies ]; }
for (var i = 0; i < existingCookies.length; i++) { var rewrittenCookie = existingCookies[i].replace(/(Domain)=[a-z\.-_]*?(;|$)/gi, '$1=' + rewriteHostname + '$2');
if (!req.connection.encrypted) { rewrittenCookie = rewrittenCookie.replace(/;\s*?(Secure)/i, ''); } rewrittenCookies.push(rewrittenCookie); }
applyTo['set-cookie'] = rewrittenCookies; }
function slashJoin(p1, p2) { var trailing_slash = false;
if (p1.length && p1[p1.length - 1] === '/') { trailing_slash = true; } if (trailing_slash && p2.length && p2[0] === '/') {p2 = p2.substring(1); }
return p1 + p2; }
function extend(obj, src) { for (var key in src) if (owns.call(src, key)) obj[key] = src[key]; return obj; }
//merges data without changing state in either argument
function merge(src1, src2) { var merged = {}; extend(merged, src1); extend(merged, src2); return merged; }
|